~ubuntu-branches/ubuntu/precise/nautilus-actions/precise

« back to all changes in this revision

Viewing changes to src/common/na-gnome-vfs-uri.c

  • Committer: Bazaar Package Importer
  • Author(s): Christine Spang
  • Date: 2009-08-12 14:04:00 UTC
  • mfrom: (1.1.6 upstream) (3.1.4 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090812140400-ufe3o3jvr62lf0sf
Tags: 1.12.0-1
* New upstream stable release.
  - Actions can now be enabled/disabled: disabled actions will
    never show up in the nautilus context menu
  - Gnome Bugzilla bugs fixed:
    - #325519 asked by Frederic Ruaudel (enabled property)
    - #590398 reported by Pierre Wieser (install doc)
    - #590399 reported by Pierre Wieser (gtk_image_menu_item_set_image)
    - #590709 reported by Claude Paroz (markup in translatable strings)
    - #590711 reported by Claude Paroz (pipe char is ambiguous to translate)
 - Do not install GConf schemas if --disable-schemas-install option has
   been specified
 - New/updated es and fr translations

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Nautilus Actions
 
3
 * A Nautilus extension which offers configurable context menu actions.
 
4
 *
 
5
 * Copyright (C) 2005 The GNOME Foundation
 
6
 * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
 
7
 * Copyright (C) 2009 Pierre Wieser and others (see AUTHORS)
 
8
 *
 
9
 * This Program is free software; you can redistribute it and/or
 
10
 * modify it under the terms of the GNU General Public License as
 
11
 * published by the Free Software Foundation; either version 2 of
 
12
 * the License, or (at your option) any later version.
 
13
 *
 
14
 * This Program 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
 
20
 * License along with this Library; see the file COPYING.  If not,
 
21
 * write to the Free Software Foundation, Inc., 59 Temple Place,
 
22
 * Suite 330, Boston, MA 02111-1307, USA.
 
23
 *
 
24
 * Authors:
 
25
 *   Frederic Ruaudel <grumz@grumz.net>
 
26
 *   Rodrigo Moya <rodrigo@gnome-db.org>
 
27
 *   Pierre Wieser <pwieser@trychlos.org>
 
28
 *   ... and many others (see AUTHORS)
 
29
 */
 
30
 
 
31
/*
 
32
 * pwi 2009-07-29
 
33
 * shamelessly pull out of GnomeVFS (gnome-vfs-uri and consorts)
 
34
 */
 
35
 
 
36
/* gnome-vfs-uri.h - URI handling for the GNOME Virtual File System.
 
37
 
 
38
   Copyright (C) 1999 Free Software Foundation
 
39
 
 
40
   The Gnome Library is free software; you can redistribute it and/or
 
41
   modify it under the terms of the GNU Library General Public License as
 
42
   published by the Free Software Foundation; either version 2 of the
 
43
   License, or (at your option) any later version.
 
44
 
 
45
   The Gnome Library is distributed in the hope that it will be useful,
 
46
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
47
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
48
   Library General Public License for more details.
 
49
 
 
50
   You should have received a copy of the GNU Library General Public
 
51
   License along with the Gnome Library; see the file COPYING.LIB.  If not,
 
52
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
53
   Boston, MA 02111-1307, USA.
 
54
 
 
55
   Author: Ettore Perazzoli <ettore@comm2000.it> */
 
56
 
 
57
#ifdef HAVE_CONFIG_H
 
58
#include <config.h>
 
59
#endif
 
60
 
 
61
#include <string.h>
 
62
 
 
63
#include "na-gnome-vfs-uri.h"
 
64
 
 
65
#define HEX_ESCAPE '%'
 
66
 
 
67
static void         collapse_slash_runs (char *path, int from_offset);
 
68
static int          find_next_slash (const char *path, int current_offset);
 
69
static int          find_slash_before_offset (const char *path, int to);
 
70
static const gchar *get_method_string (const gchar *substring, gchar **method_string);
 
71
static gchar *      gnome_vfs_canonicalize_pathname (gchar *path);
 
72
static char        *gnome_vfs_escape_set(const char *string, const char *match_set);
 
73
static void         gnome_vfs_remove_optional_escapes (char *uri);
 
74
static char *       gnome_vfs_unescape_string (const gchar *escaped_string, const gchar *illegal_characters);
 
75
static int          hex_to_int (gchar c);
 
76
static void         set_uri_element (NAGnomeVFSURI *vfs, const gchar *text, guint len);
 
77
static gchar       *split_toplevel_uri (const gchar *path, guint path_len,
 
78
                                                                                                gchar **host_return, gchar **user_return,
 
79
                                                                                                guint *port_return, gchar **password_return);
 
80
static int          unescape_character (const char *scanner);
 
81
 
 
82
void
 
83
na_gnome_vfs_uri_parse( NAGnomeVFSURI *vfs, const gchar *text_uri )
 
84
{
 
85
        const gchar *method_scanner;
 
86
        gchar *extension_scanner;
 
87
 
 
88
        vfs->uri = NULL;
 
89
        vfs->scheme = NULL;
 
90
        vfs->host_name = NULL;
 
91
        vfs->host_port = 0;
 
92
        vfs->user_name = NULL;
 
93
        vfs->password = NULL;
 
94
 
 
95
        if (text_uri[0] == '\0') {
 
96
                return;
 
97
        }
 
98
 
 
99
        method_scanner = get_method_string(text_uri, &vfs->scheme );
 
100
        if (strcmp (vfs->scheme, "pipe") == 0 ){
 
101
                return;
 
102
        }
 
103
 
 
104
        extension_scanner = strchr (method_scanner, GNOME_VFS_URI_MAGIC_CHR);
 
105
        if (extension_scanner == NULL) {
 
106
                set_uri_element (vfs, method_scanner, strlen (method_scanner));
 
107
                return;
 
108
        }
 
109
 
 
110
        /* handle '#' */
 
111
        set_uri_element (vfs, method_scanner, extension_scanner - method_scanner);
 
112
 
 
113
        if (strchr (extension_scanner, ':') == NULL) {
 
114
                /* extension is a fragment identifier */
 
115
                /*uri->fragment_id = g_strdup (extension_scanner + 1);*/
 
116
                return;
 
117
        }
 
118
}
 
119
 
 
120
void
 
121
na_gnome_vfs_uri_free( NAGnomeVFSURI *vfs )
 
122
{
 
123
        g_free( vfs->scheme );
 
124
        g_free( vfs->host_name );
 
125
        g_free( vfs->user_name );
 
126
        g_free( vfs->password );
 
127
        g_free( vfs );
 
128
}
 
129
 
 
130
static void
 
131
collapse_slash_runs (char *path, int from_offset)
 
132
{
 
133
        int i;
 
134
        /* Collapse multiple `/'s in a row. */
 
135
        for (i = from_offset;; i++) {
 
136
                if (path[i] != GNOME_VFS_URI_PATH_CHR) {
 
137
                        break;
 
138
                }
 
139
        }
 
140
 
 
141
        if (from_offset < i) {
 
142
                memmove (path + from_offset, path + i, strlen (path + i) + 1);
 
143
                i = from_offset + 1;
 
144
        }
 
145
}
 
146
 
 
147
static int
 
148
find_next_slash (const char *path, int current_offset)
 
149
{
 
150
        const char *match;
 
151
 
 
152
        g_assert (current_offset <= strlen (path));
 
153
 
 
154
        match = strchr (path + current_offset, GNOME_VFS_URI_PATH_CHR);
 
155
        return match == NULL ? -1 : match - path;
 
156
}
 
157
 
 
158
static int
 
159
find_slash_before_offset (const char *path, int to)
 
160
{
 
161
        int result;
 
162
        int next_offset;
 
163
 
 
164
        result = -1;
 
165
        next_offset = 0;
 
166
        for (;;) {
 
167
                next_offset = find_next_slash (path, next_offset);
 
168
                if (next_offset < 0 || next_offset >= to) {
 
169
                        break;
 
170
                }
 
171
                result = next_offset;
 
172
                next_offset++;
 
173
        }
 
174
        return result;
 
175
}
 
176
 
 
177
static const gchar *
 
178
get_method_string (const gchar *substring, gchar **method_string)
 
179
{
 
180
        const gchar *p;
 
181
        char *method;
 
182
 
 
183
        for (p = substring;
 
184
             g_ascii_isalnum (*p) || *p == '+' || *p == '-' || *p == '.';
 
185
             p++)
 
186
                ;
 
187
 
 
188
        if (*p == ':'
 
189
#ifdef G_OS_WIN32
 
190
                      &&
 
191
            !(p == substring + 1 && g_ascii_isalpha (*substring))
 
192
#endif
 
193
                                                                 ) {
 
194
                /* Found toplevel method specification.  */
 
195
                method = g_strndup (substring, p - substring);
 
196
                *method_string = g_ascii_strdown (method, -1);
 
197
                g_free (method);
 
198
                p++;
 
199
        } else {
 
200
                *method_string = g_strdup ("file");
 
201
                p = substring;
 
202
        }
 
203
        return p;
 
204
}
 
205
 
 
206
/* Canonicalize path, and return a new path.  Do everything in situ.  The new
 
207
   path differs from path in:
 
208
 
 
209
     Multiple `/'s are collapsed to a single `/'.
 
210
     Leading `./'s and trailing `/.'s are removed.
 
211
     Non-leading `../'s and trailing `..'s are handled by removing
 
212
     portions of the path.  */
 
213
static gchar *
 
214
gnome_vfs_canonicalize_pathname (gchar *path)
 
215
{
 
216
        int i, marker;
 
217
 
 
218
        if (path == NULL || strlen (path) == 0) {
 
219
                return "";
 
220
        }
 
221
 
 
222
        /* Walk along path looking for things to compact. */
 
223
        for (i = 0, marker = 0;;) {
 
224
                if (!path[i])
 
225
                        break;
 
226
 
 
227
                /* Check for `../', `./' or trailing `.' by itself. */
 
228
                if (path[i] == '.') {
 
229
                        /* Handle trailing `.' by itself. */
 
230
                        if (path[i + 1] == '\0') {
 
231
                                if (i > 1 && path[i - 1] == GNOME_VFS_URI_PATH_CHR) {
 
232
                                        /* strip the trailing /. */
 
233
                                        path[i - 1] = '\0';
 
234
                                } else {
 
235
                                        /* convert path "/." to "/" */
 
236
                                        path[i] = '\0';
 
237
                                }
 
238
                                break;
 
239
                        }
 
240
 
 
241
                        /* Handle `./'. */
 
242
                        if (path[i + 1] == GNOME_VFS_URI_PATH_CHR) {
 
243
                                memmove (path + i, path + i + 2,
 
244
                                         strlen (path + i + 2) + 1);
 
245
                                if (i == 0) {
 
246
                                        /* don't leave leading '/' for paths that started
 
247
                                         * as relative (.//foo)
 
248
                                         */
 
249
                                        collapse_slash_runs (path, i);
 
250
                                        marker = 0;
 
251
                                }
 
252
                                continue;
 
253
                        }
 
254
 
 
255
                        /* Handle `../' or trailing `..' by itself.
 
256
                         * Remove the previous xxx/ part
 
257
                         */
 
258
                        if (path[i + 1] == '.'
 
259
                            && (path[i + 2] == GNOME_VFS_URI_PATH_CHR
 
260
                                || path[i + 2] == '\0')) {
 
261
 
 
262
                                /* ignore ../ at the beginning of a path */
 
263
                                if (i != 0) {
 
264
                                        marker = find_slash_before_offset (path, i - 1);
 
265
 
 
266
                                        /* Either advance past '/' or point to the first character */
 
267
                                        marker ++;
 
268
                                        if (path [i + 2] == '\0' && marker > 1) {
 
269
                                                /* If we are looking at a /.. at the end of the uri and we
 
270
                                                 * need to eat the last '/' too.
 
271
                                                 */
 
272
                                                 marker--;
 
273
                                        }
 
274
                                        g_assert(marker < i);
 
275
 
 
276
                                        if (path[i + 2] == GNOME_VFS_URI_PATH_CHR) {
 
277
                                                /* strip the entire ../ string */
 
278
                                                i++;
 
279
                                        }
 
280
 
 
281
                                        memmove (path + marker, path + i + 2,
 
282
                                                 strlen (path + i + 2) + 1);
 
283
                                        i = marker;
 
284
                                } else {
 
285
                                        i = 2;
 
286
                                        if (path[i] == GNOME_VFS_URI_PATH_CHR) {
 
287
                                                i++;
 
288
                                        }
 
289
                                }
 
290
                                collapse_slash_runs (path, i);
 
291
                                continue;
 
292
                        }
 
293
                }
 
294
 
 
295
                /* advance to the next '/' */
 
296
                i = find_next_slash (path, i);
 
297
 
 
298
                /* If we didn't find any slashes, then there is nothing left to do. */
 
299
                if (i < 0) {
 
300
                        break;
 
301
                }
 
302
 
 
303
                marker = i++;
 
304
                collapse_slash_runs (path, i);
 
305
        }
 
306
        return path;
 
307
}
 
308
 
 
309
/*  Escape undesirable characters using %
 
310
 *  -------------------------------------
 
311
 *
 
312
 * This function takes a pointer to a string in which
 
313
 * some characters may be unacceptable unescaped.
 
314
 * It returns a string which has these characters
 
315
 * represented by a '%' character followed by two hex digits.
 
316
 *
 
317
 * This routine returns a g_malloced string.
 
318
 */
 
319
 
 
320
static const gchar hex[16] = "0123456789ABCDEF";
 
321
 
 
322
/**
 
323
 * gnome_vfs_escape_set:
 
324
 * @string: string to be escaped.
 
325
 * @match_set: a string containing all characters to be escaped in @string.
 
326
 *
 
327
 * Escapes all characters in @string which are listed in @match_set.
 
328
 *
 
329
 * Return value: a newly allocated string equivalent to @string but
 
330
 * with characters in @match_string escaped.
 
331
 */
 
332
static char *
 
333
gnome_vfs_escape_set (const char *string,
 
334
                      const char *match_set)
 
335
{
 
336
        char *result;
 
337
        const char *scanner;
 
338
        char *result_scanner;
 
339
        int escape_count;
 
340
 
 
341
        escape_count = 0;
 
342
 
 
343
        if (string == NULL) {
 
344
                return NULL;
 
345
        }
 
346
 
 
347
        if (match_set == NULL) {
 
348
                return g_strdup (string);
 
349
        }
 
350
 
 
351
        for (scanner = string; *scanner != '\0'; scanner++) {
 
352
                if (strchr(match_set, *scanner) != NULL) {
 
353
                        /* this character is in the set of characters
 
354
                         * we want escaped.
 
355
                         */
 
356
                        escape_count++;
 
357
                }
 
358
        }
 
359
 
 
360
        if (escape_count == 0) {
 
361
                return g_strdup (string);
 
362
        }
 
363
 
 
364
        /* allocate two extra characters for every character that
 
365
         * needs escaping and space for a trailing zero
 
366
         */
 
367
        result = g_malloc (scanner - string + escape_count * 2 + 1);
 
368
        for (scanner = string, result_scanner = result; *scanner != '\0'; scanner++) {
 
369
                if (strchr(match_set, *scanner) != NULL) {
 
370
                        /* this character is in the set of characters
 
371
                         * we want escaped.
 
372
                         */
 
373
                        *result_scanner++ = HEX_ESCAPE;
 
374
                        *result_scanner++ = hex[*scanner >> 4];
 
375
                        *result_scanner++ = hex[*scanner & 15];
 
376
 
 
377
                } else {
 
378
                        *result_scanner++ = *scanner;
 
379
                }
 
380
        }
 
381
 
 
382
        *result_scanner = '\0';
 
383
 
 
384
        return result;
 
385
}
 
386
 
 
387
/**
 
388
 * gnome_vfs_remove_optional_escapes:
 
389
 * @uri: an escaped uri.
 
390
 *
 
391
 * Scans the @uri and converts characters that do not have to be
 
392
 * escaped into an un-escaped form. The characters that get treated this
 
393
 * way are defined as unreserved by the RFC.
 
394
 *
 
395
 * Return value: an error value if the @uri is found to be malformed.
 
396
 */
 
397
 
 
398
enum {
 
399
        RESERVED = 1,
 
400
        UNRESERVED,
 
401
        DELIMITERS,
 
402
        UNWISE,
 
403
        CONTROL,
 
404
        SPACE
 
405
};
 
406
 
 
407
static const guchar uri_character_kind[128] =
 
408
{
 
409
    CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,
 
410
    CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,
 
411
    CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,
 
412
    CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,CONTROL   ,
 
413
    /* ' '        !          "          #          $          %          &          '      */
 
414
    SPACE     ,UNRESERVED,DELIMITERS,DELIMITERS,RESERVED  ,DELIMITERS,RESERVED  ,UNRESERVED,
 
415
    /*  (         )          *          +          ,          -          .          /      */
 
416
    UNRESERVED,UNRESERVED,UNRESERVED,RESERVED  ,RESERVED  ,UNRESERVED,UNRESERVED,RESERVED  ,
 
417
    /*  0         1          2          3          4          5          6          7      */
 
418
    UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,
 
419
    /*  8         9          :          ;          <          =          >          ?      */
 
420
    UNRESERVED,UNRESERVED,RESERVED  ,RESERVED  ,DELIMITERS,RESERVED  ,DELIMITERS,RESERVED  ,
 
421
    /*  @         A          B          C          D          E          F          G      */
 
422
    RESERVED  ,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,
 
423
    /*  H         I          J          K          L          M          N          O      */
 
424
    UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,
 
425
    /*  P         Q          R          S          T          U          V          W      */
 
426
    UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,
 
427
    /*  X         Y          Z          [          \          ]          ^          _      */
 
428
    UNRESERVED,UNRESERVED,UNRESERVED,UNWISE    ,UNWISE    ,UNWISE    ,UNWISE    ,UNRESERVED,
 
429
    /*  `         a          b          c          d          e          f          g      */
 
430
    UNWISE    ,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,
 
431
    /*  h         i          j          k          l          m          n          o      */
 
432
    UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,
 
433
    /*  p         q          r          s          t          u          v          w      */
 
434
    UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,
 
435
    /*  x         y          z         {           |          }          ~         DEL     */
 
436
    UNRESERVED,UNRESERVED,UNRESERVED,UNWISE    ,UNWISE    ,UNWISE    ,UNRESERVED,CONTROL
 
437
};
 
438
 
 
439
static void
 
440
gnome_vfs_remove_optional_escapes (char *uri)
 
441
{
 
442
        guchar *scanner;
 
443
        int character;
 
444
        int length;
 
445
 
 
446
        if (uri == NULL) {
 
447
                return;
 
448
        }
 
449
 
 
450
        length = strlen (uri);
 
451
 
 
452
        for (scanner = (guchar *)uri; *scanner != '\0'; scanner++, length--) {
 
453
                if (*scanner == HEX_ESCAPE) {
 
454
                        character = unescape_character ((char *)scanner + 1);
 
455
                        if (character < 0) {
 
456
                                /* invalid hexadecimal character */
 
457
                                return;
 
458
                        }
 
459
 
 
460
                        if (uri_character_kind [character] == UNRESERVED) {
 
461
                                /* This character does not need to be escaped, convert it
 
462
                                 * to a non-escaped form.
 
463
                                 */
 
464
                                *scanner = (guchar)character;
 
465
                                g_assert (length >= 3);
 
466
 
 
467
                                /* Shrink the string covering up the two extra digits of the
 
468
                                 * escaped character. Include the trailing '\0' in the copy
 
469
                                 * to keep the string terminated.
 
470
                                 */
 
471
                                memmove (scanner + 1, scanner + 3, length - 2);
 
472
                        } else {
 
473
                                /* This character must stay escaped, skip the entire
 
474
                                 * escaped sequence
 
475
                                 */
 
476
                                scanner += 2;
 
477
                        }
 
478
                        length -= 2;
 
479
 
 
480
                } else if (*scanner > 127
 
481
                        || uri_character_kind [*scanner] == DELIMITERS
 
482
                        || uri_character_kind [*scanner] == UNWISE
 
483
                        || uri_character_kind [*scanner] == CONTROL) {
 
484
                        /* It is illegal for this character to be in an un-escaped form
 
485
                         * in the uri.
 
486
                         */
 
487
                        return;
 
488
                }
 
489
        }
 
490
}
 
491
 
 
492
static int
 
493
hex_to_int (gchar c)
 
494
{
 
495
        return  c >= '0' && c <= '9' ? c - '0'
 
496
                : c >= 'A' && c <= 'F' ? c - 'A' + 10
 
497
                : c >= 'a' && c <= 'f' ? c - 'a' + 10
 
498
                : -1;
 
499
}
 
500
 
 
501
static int
 
502
unescape_character (const char *scanner)
 
503
{
 
504
        int first_digit;
 
505
        int second_digit;
 
506
 
 
507
        first_digit = hex_to_int (*scanner++);
 
508
        if (first_digit < 0) {
 
509
                return -1;
 
510
        }
 
511
 
 
512
        second_digit = hex_to_int (*scanner++);
 
513
        if (second_digit < 0) {
 
514
                return -1;
 
515
        }
 
516
 
 
517
        return (first_digit << 4) | second_digit;
 
518
}
 
519
 
 
520
/**
 
521
 * gnome_vfs_unescape_string:
 
522
 * @escaped_string: an escaped uri, path, or other string.
 
523
 * @illegal_characters: a string containing a sequence of characters
 
524
 * considered "illegal" to be escaped, '\0' is automatically in this list.
 
525
 *
 
526
 * Decodes escaped characters (i.e. PERCENTxx sequences) in @escaped_string.
 
527
 * Characters are encoded in PERCENTxy form, where xy is the ASCII hex code
 
528
 * for character 16x+y.
 
529
 *
 
530
 * Return value: a newly allocated string with the unescaped
 
531
 * equivalents, or %NULL if @escaped_string contained an escaped
 
532
 * encoding of one of the characters in @illegal_characters.
 
533
 */
 
534
static char *
 
535
gnome_vfs_unescape_string (const gchar *escaped_string,
 
536
                           const gchar *illegal_characters)
 
537
{
 
538
        const gchar *in;
 
539
        gchar *out, *result;
 
540
        gint character;
 
541
 
 
542
        if (escaped_string == NULL) {
 
543
                return NULL;
 
544
        }
 
545
 
 
546
        result = g_malloc (strlen (escaped_string) + 1);
 
547
 
 
548
        out = result;
 
549
        for (in = escaped_string; *in != '\0'; in++) {
 
550
                character = *in;
 
551
                if (*in == HEX_ESCAPE) {
 
552
                        character = unescape_character (in + 1);
 
553
 
 
554
                        /* Check for an illegal character. We consider '\0' illegal here. */
 
555
                        if (character <= 0
 
556
                            || (illegal_characters != NULL
 
557
                                && strchr (illegal_characters, (char)character) != NULL)) {
 
558
                                g_free (result);
 
559
                                return NULL;
 
560
                        }
 
561
                        in += 2;
 
562
                }
 
563
                *out++ = (char)character;
 
564
        }
 
565
 
 
566
        *out = '\0';
 
567
        g_assert (out - result <= strlen (escaped_string));
 
568
        return result;
 
569
 
 
570
}
 
571
 
 
572
static void
 
573
set_uri_element (NAGnomeVFSURI *vfs,
 
574
                 const gchar *text,
 
575
                 guint len)
 
576
{
 
577
        char *escaped_text;
 
578
 
 
579
        if (text == NULL || len == 0) {
 
580
                vfs->uri = g_strdup ("/");
 
581
                return;
 
582
        }
 
583
 
 
584
        if ( text[0] == '/' && text[1] == '/') {
 
585
                vfs->uri = split_toplevel_uri (text + 2, len - 2,
 
586
                                                &vfs->host_name,
 
587
                                                &vfs->user_name,
 
588
                                                &vfs->host_port,
 
589
                                                &vfs->password);
 
590
        } else {
 
591
                vfs->uri = g_strndup (text, len);
 
592
        }
 
593
 
 
594
        /* FIXME: this should be handled/supported by the specific method.
 
595
         * This is a quick and dirty hack to minimize the amount of changes
 
596
         * right before a milestone release.
 
597
         *
 
598
         * Do some method specific escaping. This for instance converts
 
599
         * '?' to %3F in every method except "http" where it has a special
 
600
         * meaning.
 
601
         */
 
602
        if ( ! (strcmp (vfs->scheme, "http") == 0
 
603
                || strcmp (vfs->scheme, "https") == 0
 
604
                || strcmp (vfs->scheme, "dav") == 0
 
605
                || strcmp (vfs->scheme, "davs") == 0
 
606
                || strcmp (vfs->scheme, "ghelp") == 0
 
607
                || strcmp (vfs->scheme, "gnome-help") == 0
 
608
                || strcmp (vfs->scheme, "help") == 0
 
609
                )) {
 
610
 
 
611
                escaped_text = gnome_vfs_escape_set (vfs->uri, ";?&=+$,");
 
612
                g_free (vfs->uri);
 
613
                vfs->uri = escaped_text;
 
614
        }
 
615
 
 
616
        gnome_vfs_remove_optional_escapes (vfs->uri);
 
617
        gnome_vfs_canonicalize_pathname (vfs->uri);
 
618
}
 
619
 
 
620
/*
 
621
   split_toplevel_uri
 
622
 
 
623
   Extract hostname and username from "path" with length "path_len"
 
624
 
 
625
   examples:
 
626
       sunsite.unc.edu/pub/linux
 
627
       miguel@sphinx.nuclecu.unam.mx/c/nc
 
628
       tsx-11.mit.edu:8192/
 
629
       joe@foo.edu:11321/private
 
630
       joe:password@foo.se
 
631
 
 
632
   This function implements the following regexp: (whitespace for clarity)
 
633
 
 
634
   ( ( ([^:@/]*) (:[^@/]*)? @ )? ([^/:]*) (:([0-9]*)?) )?  (/.*)?
 
635
   ( ( ( user  ) (  pw  )?   )?   (host)    (port)?   )? (path <return value>)?
 
636
 
 
637
  It returns NULL if neither <host> nor <path> could be matched.
 
638
 
 
639
  port is checked to ensure that it does not exceed 0xffff.
 
640
 
 
641
  return value is <path> or is "/" if the path portion is not present
 
642
  All other arguments are set to 0 or NULL if their portions are not present
 
643
 
 
644
  pedantic: this function ends up doing an unbounded lookahead, making it
 
645
  potentially O(n^2) instead of O(n).  This could be avoided.  Realistically, though,
 
646
  its just the password field.
 
647
 
 
648
  Differences between the old and the new implemention:
 
649
 
 
650
                     Old                     New
 
651
  localhost:8080     host="localhost:8080"   host="localhost" port=8080
 
652
  /Users/mikef       host=""                 host=NULL
 
653
 
 
654
*/
 
655
 
 
656
 
 
657
#define URI_MOVE_PAST_DELIMITER \
 
658
        do {                                                    \
 
659
                cur_tok_start = (++cur);                        \
 
660
                if (path_end == cur) {                          \
 
661
                        success = FALSE;                        \
 
662
                        goto done;                              \
 
663
                }                                               \
 
664
        } while (0);
 
665
 
 
666
 
 
667
#define uri_strlen_to(from, to)  ( (to) - (from) )
 
668
#define uri_strdup_to(from, to)  g_strndup ((from), uri_strlen_to((from), (to)))
 
669
 
 
670
typedef struct {
 
671
        const char *chrs;
 
672
        gboolean primed;
 
673
        char bv[32];
 
674
} UriStrspnSet;
 
675
 
 
676
static UriStrspnSet uri_strspn_sets[] = {
 
677
        {":@]" GNOME_VFS_URI_PATH_STR, FALSE, ""},
 
678
        {"@" GNOME_VFS_URI_PATH_STR, FALSE, ""},
 
679
        {":" GNOME_VFS_URI_PATH_STR, FALSE, ""},
 
680
        {"]" GNOME_VFS_URI_PATH_STR, FALSE, ""}
 
681
};
 
682
 
 
683
#define URI_DELIMITER_ALL_SET (uri_strspn_sets + 0)
 
684
#define URI_DELIMITER_USER_SET (uri_strspn_sets + 1)
 
685
#define URI_DELIMITER_HOST_SET (uri_strspn_sets + 2)
 
686
#define URI_DELIMITER_IPV6_SET (uri_strspn_sets + 3)
 
687
 
 
688
#define BV_SET(bv, idx) (bv)[((guchar)(idx))>>3] |= (1 << ( (idx) & 7) )
 
689
#define BV_IS_SET(bv, idx) ((bv)[((guchar)(idx))>>3] & (1 << ( (idx) & 7)))
 
690
 
 
691
static const char *
 
692
uri_strspn_to(const char *str, UriStrspnSet *set, const char *path_end)
 
693
{
 
694
        const char *cur;
 
695
        const char *cur_chr;
 
696
 
 
697
        if (!set->primed) {
 
698
                memset (set->bv, 0, sizeof(set->bv));
 
699
 
 
700
                for (cur_chr = set->chrs; '\0' != *cur_chr; cur_chr++) {
 
701
                        BV_SET (set->bv, *cur_chr);
 
702
                }
 
703
 
 
704
                BV_SET (set->bv, '\0');
 
705
                set->primed = TRUE;
 
706
        }
 
707
 
 
708
        for (cur = str; cur < path_end && ! BV_IS_SET (set->bv, *cur); cur++)
 
709
                ;
 
710
 
 
711
        if (cur >= path_end || '\0' == *cur) {
 
712
                return NULL;
 
713
        }
 
714
 
 
715
        return cur;
 
716
}
 
717
 
 
718
static gchar *
 
719
split_toplevel_uri (const gchar *path, guint path_len,
 
720
                    gchar **host_return, gchar **user_return,
 
721
                    guint *port_return, gchar **password_return)
 
722
{
 
723
        const char *path_end;
 
724
        const char *cur_tok_start;
 
725
        const char *cur;
 
726
        const char *next_delimiter;
 
727
        char *ret;
 
728
        char *host;
 
729
        gboolean success;
 
730
 
 
731
        g_assert (host_return != NULL);
 
732
        g_assert (user_return != NULL);
 
733
        g_assert (port_return != NULL);
 
734
        g_assert (password_return != NULL);
 
735
 
 
736
        *host_return = NULL;
 
737
        *user_return = NULL;
 
738
        *port_return = 0;
 
739
        *password_return = NULL;
 
740
        ret = NULL;
 
741
 
 
742
        success = FALSE;
 
743
 
 
744
        if (path == NULL || path_len == 0) {
 
745
                return g_strdup ("/");
 
746
        }
 
747
 
 
748
 
 
749
        path_end = path + path_len;
 
750
 
 
751
        cur_tok_start = path;
 
752
        cur = uri_strspn_to (cur_tok_start, URI_DELIMITER_ALL_SET, path_end);
 
753
 
 
754
        if (cur != NULL) {
 
755
                const char *tmp;
 
756
 
 
757
                if (*cur == ':') {
 
758
                        /* This ':' belongs to username or IPv6 address.*/
 
759
                        tmp = uri_strspn_to (cur_tok_start, URI_DELIMITER_USER_SET, path_end);
 
760
 
 
761
                        if (tmp == NULL || *tmp != '@') {
 
762
                                tmp = uri_strspn_to (cur_tok_start, URI_DELIMITER_IPV6_SET, path_end);
 
763
 
 
764
                                if (tmp != NULL && *tmp == ']') {
 
765
                                        cur = tmp;
 
766
                                }
 
767
                        }
 
768
                }
 
769
        }
 
770
 
 
771
        if (cur != NULL) {
 
772
 
 
773
                /* Check for IPv6 address. */
 
774
                if (*cur == ']') {
 
775
 
 
776
                        /*  No username:password in the URI  */
 
777
                        /*  cur points to ']'  */
 
778
 
 
779
                        cur = uri_strspn_to (cur, URI_DELIMITER_HOST_SET, path_end);
 
780
                }
 
781
        }
 
782
 
 
783
        if (cur != NULL) {
 
784
                next_delimiter = uri_strspn_to (cur, URI_DELIMITER_USER_SET, path_end);
 
785
        } else {
 
786
                next_delimiter = NULL;
 
787
        }
 
788
 
 
789
        if (cur != NULL
 
790
                && (*cur == '@'
 
791
                    || (next_delimiter != NULL && *next_delimiter != '/' ))) {
 
792
 
 
793
                /* *cur == ':' or '@' and string contains a @ before a / */
 
794
 
 
795
                if (uri_strlen_to (cur_tok_start, cur) > 0) {
 
796
                        char *tmp;
 
797
                        tmp = uri_strdup_to (cur_tok_start,cur);
 
798
                        *user_return = gnome_vfs_unescape_string (tmp, NULL);
 
799
                        g_free (tmp);
 
800
                }
 
801
 
 
802
                if (*cur == ':') {
 
803
                        URI_MOVE_PAST_DELIMITER;
 
804
 
 
805
                        cur = uri_strspn_to(cur_tok_start, URI_DELIMITER_USER_SET, path_end);
 
806
 
 
807
                        if (cur == NULL || *cur != '@') {
 
808
                                success = FALSE;
 
809
                                goto done;
 
810
                        } else if (uri_strlen_to (cur_tok_start, cur) > 0) {
 
811
                                char *tmp;
 
812
                                tmp = uri_strdup_to (cur_tok_start,cur);
 
813
                                *password_return = gnome_vfs_unescape_string (tmp, NULL);
 
814
                                g_free (tmp);
 
815
                        }
 
816
                }
 
817
 
 
818
                if (*cur != '/') {
 
819
                        URI_MOVE_PAST_DELIMITER;
 
820
 
 
821
                        /* Move cur to point to ':' after ']' */
 
822
                        cur = uri_strspn_to (cur_tok_start, URI_DELIMITER_IPV6_SET, path_end);
 
823
 
 
824
                        if (cur != NULL && *cur == ']') {  /* For IPv6 address */
 
825
                                cur = uri_strspn_to (cur, URI_DELIMITER_HOST_SET, path_end);
 
826
                        } else {
 
827
                                cur = uri_strspn_to (cur_tok_start, URI_DELIMITER_HOST_SET, path_end);
 
828
                        }
 
829
                } else {
 
830
                        cur_tok_start = cur;
 
831
                }
 
832
        }
 
833
 
 
834
        if (cur == NULL) {
 
835
                /* [^:/]+$ */
 
836
                if (uri_strlen_to (cur_tok_start, path_end) > 0) {
 
837
                        *host_return = uri_strdup_to (cur_tok_start, path_end);
 
838
                        if (*(path_end - 1) == GNOME_VFS_URI_PATH_CHR) {
 
839
                                ret = g_strdup (GNOME_VFS_URI_PATH_STR);
 
840
                        } else {
 
841
                                ret = g_strdup ("");
 
842
                        }
 
843
                        success = TRUE;
 
844
                } else { /* No host, no path */
 
845
                        success = FALSE;
 
846
                }
 
847
 
 
848
                goto done;
 
849
 
 
850
        } else if (*cur == ':') {
 
851
                guint port;
 
852
                /* [^:/]*:.* */
 
853
 
 
854
                if (uri_strlen_to (cur_tok_start, cur) > 0) {
 
855
                        *host_return = uri_strdup_to (cur_tok_start, cur);
 
856
                } else {
 
857
                        success = FALSE;
 
858
                        goto done;      /*No host but a port?*/
 
859
                }
 
860
 
 
861
                URI_MOVE_PAST_DELIMITER;
 
862
 
 
863
                port = 0;
 
864
 
 
865
                for ( ; cur < path_end && g_ascii_isdigit (*cur); cur++) {
 
866
                        port *= 10;
 
867
                        port += *cur - '0';
 
868
                }
 
869
 
 
870
                /* We let :(/.*)$ be treated gracefully */
 
871
                if (*cur != '\0' && *cur != GNOME_VFS_URI_PATH_CHR) {
 
872
                        success = FALSE;
 
873
                        goto done;      /* ...but this would be an error */
 
874
                }
 
875
 
 
876
                if (port > 0xffff) {
 
877
                        success = FALSE;
 
878
                        goto done;
 
879
                }
 
880
 
 
881
                *port_return = port;
 
882
 
 
883
                cur_tok_start = cur;
 
884
 
 
885
        } else /* GNOME_VFS_URI_PATH_CHR == *cur */ {
 
886
                /* ^[^:@/]+/.*$ */
 
887
 
 
888
                if (uri_strlen_to (cur_tok_start, cur) > 0) {
 
889
                        *host_return = uri_strdup_to (cur_tok_start, cur);
 
890
                }
 
891
 
 
892
                cur_tok_start = cur;
 
893
        }
 
894
 
 
895
        if (*cur_tok_start != '\0' && uri_strlen_to (cur_tok_start, path_end) > 0) {
 
896
                ret = uri_strdup_to(cur, path_end);
 
897
        } else if (*host_return != NULL) {
 
898
                ret = g_strdup (GNOME_VFS_URI_PATH_STR);
 
899
        }
 
900
 
 
901
        success = TRUE;
 
902
 
 
903
done:
 
904
        if (*host_return != NULL) {
 
905
 
 
906
                /* Check for an IPv6 address in square brackets.*/
 
907
                if (strchr (*host_return, '[') && strchr (*host_return, ']') && strchr (*host_return, ':')) {
 
908
 
 
909
                        /* Extract the IPv6 address from square braced string. */
 
910
                        host = g_ascii_strdown ((*host_return) + 1, strlen (*host_return) - 2);
 
911
                } else {
 
912
                        host = g_ascii_strdown (*host_return, -1);
 
913
                }
 
914
 
 
915
                g_free (*host_return);
 
916
                *host_return = host;
 
917
 
 
918
        }
 
919
 
 
920
        /* If we didn't complete our mission, discard all the partials */
 
921
        if (!success) {
 
922
                g_free (*host_return);
 
923
                g_free (*user_return);
 
924
                g_free (*password_return);
 
925
                g_free (ret);
 
926
 
 
927
                *host_return = NULL;
 
928
                *user_return = NULL;
 
929
                *port_return = 0;
 
930
                *password_return = NULL;
 
931
                ret = NULL;
 
932
        }
 
933
 
 
934
        return ret;
 
935
}