Define new 'pivot-prop-loadable' property
[nautilus-actions.git] / src / core / na-gnome-vfs-uri.c
blobdba4e08a60f80284fcd22e6e088fb162331cef12
1 /*
2 * Nautilus-Actions
3 * A Nautilus extension which offers configurable context menu actions.
5 * Copyright (C) 2005 The GNOME Foundation
6 * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
7 * Copyright (C) 2009, 2010, 2011 Pierre Wieser and others (see AUTHORS)
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.
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.
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.
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)
32 * pwi 2009-07-29
33 * shamelessly pull out of GnomeVFS (gnome-vfs-uri and consorts)
36 /* gnome-vfs-uri.h - URI handling for the GNOME Virtual File System.
38 Copyright (C) 1999 Free Software Foundation
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.
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.
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.
55 Author: Ettore Perazzoli <ettore@comm2000.it> */
57 #ifdef HAVE_CONFIG_H
58 #include <config.h>
59 #endif
61 #include <string.h>
63 #include "na-gnome-vfs-uri.h"
65 #define HEX_ESCAPE '%'
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);
82 void
83 na_gnome_vfs_uri_parse( NAGnomeVFSURI *vfs, const gchar *text_uri )
85 const gchar *method_scanner;
86 gchar *extension_scanner;
88 vfs->path = NULL;
89 vfs->scheme = NULL;
90 vfs->host_name = NULL;
91 vfs->host_port = 0;
92 vfs->user_name = NULL;
93 vfs->password = NULL;
95 if (text_uri[0] == '\0') {
96 return;
99 method_scanner = get_method_string(text_uri, &vfs->scheme );
100 if (strcmp (vfs->scheme, "pipe") == 0 ){
101 return;
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;
110 /* handle '#' */
111 set_uri_element (vfs, method_scanner, extension_scanner - method_scanner);
113 if (strchr (extension_scanner, ':') == NULL) {
114 /* extension is a fragment identifier */
115 /*uri->fragment_id = g_strdup (extension_scanner + 1);*/
116 return;
120 void
121 na_gnome_vfs_uri_free( NAGnomeVFSURI *vfs )
123 g_free( vfs->path );
124 g_free( vfs->scheme );
125 g_free( vfs->host_name );
126 g_free( vfs->user_name );
127 g_free( vfs->password );
128 g_free( vfs );
131 static void
132 collapse_slash_runs (char *path, int from_offset)
134 int i;
135 /* Collapse multiple `/'s in a row. */
136 for (i = from_offset;; i++) {
137 if (path[i] != GNOME_VFS_URI_PATH_CHR) {
138 break;
142 if (from_offset < i) {
143 memmove (path + from_offset, path + i, strlen (path + i) + 1);
144 i = from_offset + 1;
148 static int
149 find_next_slash (const char *path, int current_offset)
151 const char *match;
153 g_assert (current_offset <= strlen (path));
155 match = strchr (path + current_offset, GNOME_VFS_URI_PATH_CHR);
156 return match == NULL ? -1 : match - path;
159 static int
160 find_slash_before_offset (const char *path, int to)
162 int result;
163 int next_offset;
165 result = -1;
166 next_offset = 0;
167 for (;;) {
168 next_offset = find_next_slash (path, next_offset);
169 if (next_offset < 0 || next_offset >= to) {
170 break;
172 result = next_offset;
173 next_offset++;
175 return result;
178 static const gchar *
179 get_method_string (const gchar *substring, gchar **method_string)
181 const gchar *p;
182 char *method;
184 for (p = substring;
185 g_ascii_isalnum (*p) || *p == '+' || *p == '-' || *p == '.';
186 p++)
189 if (*p == ':'
190 #ifdef G_OS_WIN32
192 !(p == substring + 1 && g_ascii_isalpha (*substring))
193 #endif
195 /* Found toplevel method specification. */
196 method = g_strndup (substring, p - substring);
197 *method_string = g_ascii_strdown (method, -1);
198 g_free (method);
199 p++;
200 } else {
201 *method_string = g_strdup ("file");
202 p = substring;
204 return p;
207 /* Canonicalize path, and return a new path. Do everything in situ. The new
208 path differs from path in:
210 Multiple `/'s are collapsed to a single `/'.
211 Leading `./'s and trailing `/.'s are removed.
212 Non-leading `../'s and trailing `..'s are handled by removing
213 portions of the path. */
214 static gchar *
215 gnome_vfs_canonicalize_pathname (gchar *path)
217 int i, marker;
219 if (path == NULL || strlen (path) == 0) {
220 return "";
223 /* Walk along path looking for things to compact. */
224 for (i = 0, marker = 0;;) {
225 if (!path[i])
226 break;
228 /* Check for `../', `./' or trailing `.' by itself. */
229 if (path[i] == '.') {
230 /* Handle trailing `.' by itself. */
231 if (path[i + 1] == '\0') {
232 if (i > 1 && path[i - 1] == GNOME_VFS_URI_PATH_CHR) {
233 /* strip the trailing /. */
234 path[i - 1] = '\0';
235 } else {
236 /* convert path "/." to "/" */
237 path[i] = '\0';
239 break;
242 /* Handle `./'. */
243 if (path[i + 1] == GNOME_VFS_URI_PATH_CHR) {
244 memmove (path + i, path + i + 2,
245 strlen (path + i + 2) + 1);
246 if (i == 0) {
247 /* don't leave leading '/' for paths that started
248 * as relative (.//foo)
250 collapse_slash_runs (path, i);
251 marker = 0;
253 continue;
256 /* Handle `../' or trailing `..' by itself.
257 * Remove the previous xxx/ part
259 if (path[i + 1] == '.'
260 && (path[i + 2] == GNOME_VFS_URI_PATH_CHR
261 || path[i + 2] == '\0')) {
263 /* ignore ../ at the beginning of a path */
264 if (i != 0) {
265 marker = find_slash_before_offset (path, i - 1);
267 /* Either advance past '/' or point to the first character */
268 marker ++;
269 if (path [i + 2] == '\0' && marker > 1) {
270 /* If we are looking at a /.. at the end of the uri and we
271 * need to eat the last '/' too.
273 marker--;
275 g_assert(marker < i);
277 if (path[i + 2] == GNOME_VFS_URI_PATH_CHR) {
278 /* strip the entire ../ string */
279 i++;
282 memmove (path + marker, path + i + 2,
283 strlen (path + i + 2) + 1);
284 i = marker;
285 } else {
286 i = 2;
287 if (path[i] == GNOME_VFS_URI_PATH_CHR) {
288 i++;
291 collapse_slash_runs (path, i);
292 continue;
296 /* advance to the next '/' */
297 i = find_next_slash (path, i);
299 /* If we didn't find any slashes, then there is nothing left to do. */
300 if (i < 0) {
301 break;
304 marker = i++;
305 collapse_slash_runs (path, i);
307 return path;
310 /* Escape undesirable characters using %
311 * -------------------------------------
313 * This function takes a pointer to a string in which
314 * some characters may be unacceptable unescaped.
315 * It returns a string which has these characters
316 * represented by a '%' character followed by two hex digits.
318 * This routine returns a g_malloced string.
321 static const gchar hex[16] = "0123456789ABCDEF";
324 * gnome_vfs_escape_set:
325 * @string: string to be escaped.
326 * @match_set: a string containing all characters to be escaped in @string.
328 * Escapes all characters in @string which are listed in @match_set.
330 * Return value: a newly allocated string equivalent to @string but
331 * with characters in @match_string escaped.
333 static char *
334 gnome_vfs_escape_set (const char *string,
335 const char *match_set)
337 char *result;
338 const char *scanner;
339 char *result_scanner;
340 int escape_count;
342 escape_count = 0;
344 if (string == NULL) {
345 return NULL;
348 if (match_set == NULL) {
349 return g_strdup (string);
352 for (scanner = string; *scanner != '\0'; scanner++) {
353 if (strchr(match_set, *scanner) != NULL) {
354 /* this character is in the set of characters
355 * we want escaped.
357 escape_count++;
361 if (escape_count == 0) {
362 return g_strdup (string);
365 /* allocate two extra characters for every character that
366 * needs escaping and space for a trailing zero
368 result = g_malloc (scanner - string + escape_count * 2 + 1);
369 for (scanner = string, result_scanner = result; *scanner != '\0'; scanner++) {
370 if (strchr(match_set, *scanner) != NULL) {
371 /* this character is in the set of characters
372 * we want escaped.
374 *result_scanner++ = HEX_ESCAPE;
375 *result_scanner++ = hex[*scanner >> 4];
376 *result_scanner++ = hex[*scanner & 15];
378 } else {
379 *result_scanner++ = *scanner;
383 *result_scanner = '\0';
385 return result;
389 * gnome_vfs_remove_optional_escapes:
390 * @uri: an escaped uri.
392 * Scans the @uri and converts characters that do not have to be
393 * escaped into an un-escaped form. The characters that get treated this
394 * way are defined as unreserved by the RFC.
396 * Return value: an error value if the @uri is found to be malformed.
399 enum {
400 RESERVED = 1,
401 UNRESERVED,
402 DELIMITERS,
403 UNWISE,
404 CONTROL,
405 SPACE
408 static const guchar uri_character_kind[128] =
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 CONTROL ,CONTROL ,CONTROL ,CONTROL ,CONTROL ,CONTROL ,CONTROL ,CONTROL ,
414 /* ' ' ! " # $ % & ' */
415 SPACE ,UNRESERVED,DELIMITERS,DELIMITERS,RESERVED ,DELIMITERS,RESERVED ,UNRESERVED,
416 /* ( ) * + , - . / */
417 UNRESERVED,UNRESERVED,UNRESERVED,RESERVED ,RESERVED ,UNRESERVED,UNRESERVED,RESERVED ,
418 /* 0 1 2 3 4 5 6 7 */
419 UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,
420 /* 8 9 : ; < = > ? */
421 UNRESERVED,UNRESERVED,RESERVED ,RESERVED ,DELIMITERS,RESERVED ,DELIMITERS,RESERVED ,
422 /* @ A B C D E F G */
423 RESERVED ,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,
424 /* H I J K L M N O */
425 UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,
426 /* P Q R S T U V W */
427 UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,
428 /* X Y Z [ \ ] ^ _ */
429 UNRESERVED,UNRESERVED,UNRESERVED,UNWISE ,UNWISE ,UNWISE ,UNWISE ,UNRESERVED,
430 /* ` a b c d e f g */
431 UNWISE ,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,
432 /* h i j k l m n o */
433 UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,
434 /* p q r s t u v w */
435 UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,UNRESERVED,
436 /* x y z { | } ~ DEL */
437 UNRESERVED,UNRESERVED,UNRESERVED,UNWISE ,UNWISE ,UNWISE ,UNRESERVED,CONTROL
440 static void
441 gnome_vfs_remove_optional_escapes (char *uri)
443 guchar *scanner;
444 int character;
445 int length;
447 if (uri == NULL) {
448 return;
451 length = strlen (uri);
453 for (scanner = (guchar *)uri; *scanner != '\0'; scanner++, length--) {
454 if (*scanner == HEX_ESCAPE) {
455 character = unescape_character ((char *)scanner + 1);
456 if (character < 0) {
457 /* invalid hexadecimal character */
458 return;
461 if (uri_character_kind [character] == UNRESERVED) {
462 /* This character does not need to be escaped, convert it
463 * to a non-escaped form.
465 *scanner = (guchar)character;
466 g_assert (length >= 3);
468 /* Shrink the string covering up the two extra digits of the
469 * escaped character. Include the trailing '\0' in the copy
470 * to keep the string terminated.
472 memmove (scanner + 1, scanner + 3, length - 2);
473 } else {
474 /* This character must stay escaped, skip the entire
475 * escaped sequence
477 scanner += 2;
479 length -= 2;
481 } else if (*scanner > 127
482 || uri_character_kind [*scanner] == DELIMITERS
483 || uri_character_kind [*scanner] == UNWISE
484 || uri_character_kind [*scanner] == CONTROL) {
485 /* It is illegal for this character to be in an un-escaped form
486 * in the uri.
488 return;
493 static int
494 hex_to_int (gchar c)
496 return c >= '0' && c <= '9' ? c - '0'
497 : c >= 'A' && c <= 'F' ? c - 'A' + 10
498 : c >= 'a' && c <= 'f' ? c - 'a' + 10
499 : -1;
502 static int
503 unescape_character (const char *scanner)
505 int first_digit;
506 int second_digit;
508 first_digit = hex_to_int (*scanner++);
509 if (first_digit < 0) {
510 return -1;
513 second_digit = hex_to_int (*scanner++);
514 if (second_digit < 0) {
515 return -1;
518 return (first_digit << 4) | second_digit;
522 * gnome_vfs_unescape_string:
523 * @escaped_string: an escaped uri, path, or other string.
524 * @illegal_characters: a string containing a sequence of characters
525 * considered "illegal" to be escaped, '\0' is automatically in this list.
527 * Decodes escaped characters (i.e. PERCENTxx sequences) in @escaped_string.
528 * Characters are encoded in PERCENTxy form, where xy is the ASCII hex code
529 * for character 16x+y.
531 * Return value: a newly allocated string with the unescaped
532 * equivalents, or %NULL if @escaped_string contained an escaped
533 * encoding of one of the characters in @illegal_characters.
535 static char *
536 gnome_vfs_unescape_string (const gchar *escaped_string,
537 const gchar *illegal_characters)
539 const gchar *in;
540 gchar *out, *result;
541 gint character;
543 if (escaped_string == NULL) {
544 return NULL;
547 result = g_malloc (strlen (escaped_string) + 1);
549 out = result;
550 for (in = escaped_string; *in != '\0'; in++) {
551 character = *in;
552 if (*in == HEX_ESCAPE) {
553 character = unescape_character (in + 1);
555 /* Check for an illegal character. We consider '\0' illegal here. */
556 if (character <= 0
557 || (illegal_characters != NULL
558 && strchr (illegal_characters, (char)character) != NULL)) {
559 g_free (result);
560 return NULL;
562 in += 2;
564 *out++ = (char)character;
567 *out = '\0';
568 g_assert (out - result <= strlen (escaped_string));
569 return result;
573 static void
574 set_uri_element (NAGnomeVFSURI *vfs,
575 const gchar *text,
576 guint len)
578 char *escaped_text;
580 if (text == NULL || len == 0) {
581 vfs->path = g_strdup ("/");
582 return;
585 if ( text[0] == '/' && text[1] == '/') {
586 vfs->path = split_toplevel_uri (text + 2, len - 2,
587 &vfs->host_name,
588 &vfs->user_name,
589 &vfs->host_port,
590 &vfs->password);
591 } else {
592 vfs->path = g_strndup (text, len);
595 /* FIXME: this should be handled/supported by the specific method.
596 * This is a quick and dirty hack to minimize the amount of changes
597 * right before a milestone release.
599 * Do some method specific escaping. This for instance converts
600 * '?' to %3F in every method except "http" where it has a special
601 * meaning.
603 if ( ! (strcmp (vfs->scheme, "http") == 0
604 || strcmp (vfs->scheme, "https") == 0
605 || strcmp (vfs->scheme, "dav") == 0
606 || strcmp (vfs->scheme, "davs") == 0
607 || strcmp (vfs->scheme, "ghelp") == 0
608 || strcmp (vfs->scheme, "gnome-help") == 0
609 || strcmp (vfs->scheme, "help") == 0
610 )) {
612 escaped_text = gnome_vfs_escape_set (vfs->path, ";?&=+$,");
613 g_free (vfs->path);
614 vfs->path = escaped_text;
617 gnome_vfs_remove_optional_escapes (vfs->path);
618 gnome_vfs_canonicalize_pathname (vfs->path);
622 split_toplevel_uri
624 Extract hostname and username from "path" with length "path_len"
626 examples:
627 sunsite.unc.edu/pub/linux
628 miguel@sphinx.nuclecu.unam.mx/c/nc
629 tsx-11.mit.edu:8192/
630 joe@foo.edu:11321/private
631 joe:password@foo.se
633 This function implements the following regexp: (whitespace for clarity)
635 ( ( ([^:@/]*) (:[^@/]*)? @ )? ([^/:]*) (:([0-9]*)?) )? (/.*)?
636 ( ( ( user ) ( pw )? )? (host) (port)? )? (path <return value>)?
638 It returns NULL if neither <host> nor <path> could be matched.
640 port is checked to ensure that it does not exceed 0xffff.
642 return value is <path> or is "/" if the path portion is not present
643 All other arguments are set to 0 or NULL if their portions are not present
645 pedantic: this function ends up doing an unbounded lookahead, making it
646 potentially O(n^2) instead of O(n). This could be avoided. Realistically, though,
647 its just the password field.
649 Differences between the old and the new implemention:
651 Old New
652 localhost:8080 host="localhost:8080" host="localhost" port=8080
653 /Users/mikef host="" host=NULL
658 #define URI_MOVE_PAST_DELIMITER \
659 do { \
660 cur_tok_start = (++cur); \
661 if (path_end == cur) { \
662 success = FALSE; \
663 goto done; \
665 } while (0);
668 #define uri_strlen_to(from, to) ( (to) - (from) )
669 #define uri_strdup_to(from, to) g_strndup ((from), uri_strlen_to((from), (to)))
671 typedef struct {
672 const char *chrs;
673 gboolean primed;
674 char bv[32];
675 } UriStrspnSet;
677 static UriStrspnSet uri_strspn_sets[] = {
678 {":@]" GNOME_VFS_URI_PATH_STR, FALSE, ""},
679 {"@" GNOME_VFS_URI_PATH_STR, FALSE, ""},
680 {":" GNOME_VFS_URI_PATH_STR, FALSE, ""},
681 {"]" GNOME_VFS_URI_PATH_STR, FALSE, ""}
684 #define URI_DELIMITER_ALL_SET (uri_strspn_sets + 0)
685 #define URI_DELIMITER_USER_SET (uri_strspn_sets + 1)
686 #define URI_DELIMITER_HOST_SET (uri_strspn_sets + 2)
687 #define URI_DELIMITER_IPV6_SET (uri_strspn_sets + 3)
689 #define BV_SET(bv, idx) (bv)[((guchar)(idx))>>3] |= (1 << ( (idx) & 7) )
690 #define BV_IS_SET(bv, idx) ((bv)[((guchar)(idx))>>3] & (1 << ( (idx) & 7)))
692 static const char *
693 uri_strspn_to(const char *str, UriStrspnSet *set, const char *path_end)
695 const char *cur;
696 const char *cur_chr;
698 if (!set->primed) {
699 memset (set->bv, 0, sizeof(set->bv));
701 for (cur_chr = set->chrs; '\0' != *cur_chr; cur_chr++) {
702 BV_SET (set->bv, *cur_chr);
705 BV_SET (set->bv, '\0');
706 set->primed = TRUE;
709 for (cur = str; cur < path_end && ! BV_IS_SET (set->bv, *cur); cur++)
712 if (cur >= path_end || '\0' == *cur) {
713 return NULL;
716 return cur;
719 static gchar *
720 split_toplevel_uri (const gchar *path, guint path_len,
721 gchar **host_return, gchar **user_return,
722 guint *port_return, gchar **password_return)
724 const char *path_end;
725 const char *cur_tok_start;
726 const char *cur;
727 const char *next_delimiter;
728 char *ret;
729 char *host;
730 gboolean success;
732 g_assert (host_return != NULL);
733 g_assert (user_return != NULL);
734 g_assert (port_return != NULL);
735 g_assert (password_return != NULL);
737 *host_return = NULL;
738 *user_return = NULL;
739 *port_return = 0;
740 *password_return = NULL;
741 ret = NULL;
743 success = FALSE;
745 if (path == NULL || path_len == 0) {
746 return g_strdup ("/");
750 path_end = path + path_len;
752 cur_tok_start = path;
753 cur = uri_strspn_to (cur_tok_start, URI_DELIMITER_ALL_SET, path_end);
755 if (cur != NULL) {
756 const char *tmp;
758 if (*cur == ':') {
759 /* This ':' belongs to username or IPv6 address.*/
760 tmp = uri_strspn_to (cur_tok_start, URI_DELIMITER_USER_SET, path_end);
762 if (tmp == NULL || *tmp != '@') {
763 tmp = uri_strspn_to (cur_tok_start, URI_DELIMITER_IPV6_SET, path_end);
765 if (tmp != NULL && *tmp == ']') {
766 cur = tmp;
772 if (cur != NULL) {
774 /* Check for IPv6 address. */
775 if (*cur == ']') {
777 /* No username:password in the URI */
778 /* cur points to ']' */
780 cur = uri_strspn_to (cur, URI_DELIMITER_HOST_SET, path_end);
784 if (cur != NULL) {
785 next_delimiter = uri_strspn_to (cur, URI_DELIMITER_USER_SET, path_end);
786 } else {
787 next_delimiter = NULL;
790 if (cur != NULL
791 && (*cur == '@'
792 || (next_delimiter != NULL && *next_delimiter != '/' ))) {
794 /* *cur == ':' or '@' and string contains a @ before a / */
796 if (uri_strlen_to (cur_tok_start, cur) > 0) {
797 char *tmp;
798 tmp = uri_strdup_to (cur_tok_start,cur);
799 *user_return = gnome_vfs_unescape_string (tmp, NULL);
800 g_free (tmp);
803 if (*cur == ':') {
804 URI_MOVE_PAST_DELIMITER;
806 cur = uri_strspn_to(cur_tok_start, URI_DELIMITER_USER_SET, path_end);
808 if (cur == NULL || *cur != '@') {
809 success = FALSE;
810 goto done;
811 } else if (uri_strlen_to (cur_tok_start, cur) > 0) {
812 char *tmp;
813 tmp = uri_strdup_to (cur_tok_start,cur);
814 *password_return = gnome_vfs_unescape_string (tmp, NULL);
815 g_free (tmp);
819 if (*cur != '/') {
820 URI_MOVE_PAST_DELIMITER;
822 /* Move cur to point to ':' after ']' */
823 cur = uri_strspn_to (cur_tok_start, URI_DELIMITER_IPV6_SET, path_end);
825 if (cur != NULL && *cur == ']') { /* For IPv6 address */
826 cur = uri_strspn_to (cur, URI_DELIMITER_HOST_SET, path_end);
827 } else {
828 cur = uri_strspn_to (cur_tok_start, URI_DELIMITER_HOST_SET, path_end);
830 } else {
831 cur_tok_start = cur;
835 if (cur == NULL) {
836 /* [^:/]+$ */
837 if (uri_strlen_to (cur_tok_start, path_end) > 0) {
838 *host_return = uri_strdup_to (cur_tok_start, path_end);
839 if (*(path_end - 1) == GNOME_VFS_URI_PATH_CHR) {
840 ret = g_strdup (GNOME_VFS_URI_PATH_STR);
841 } else {
842 ret = g_strdup ("");
844 success = TRUE;
845 } else { /* No host, no path */
846 success = FALSE;
849 goto done;
851 } else if (*cur == ':') {
852 guint port;
853 /* [^:/]*:.* */
855 if (uri_strlen_to (cur_tok_start, cur) > 0) {
856 *host_return = uri_strdup_to (cur_tok_start, cur);
857 } else {
858 success = FALSE;
859 goto done; /*No host but a port?*/
862 URI_MOVE_PAST_DELIMITER;
864 port = 0;
866 for ( ; cur < path_end && g_ascii_isdigit (*cur); cur++) {
867 port *= 10;
868 port += *cur - '0';
871 /* We let :(/.*)$ be treated gracefully */
872 if (*cur != '\0' && *cur != GNOME_VFS_URI_PATH_CHR) {
873 success = FALSE;
874 goto done; /* ...but this would be an error */
877 if (port > 0xffff) {
878 success = FALSE;
879 goto done;
882 *port_return = port;
884 cur_tok_start = cur;
886 } else /* GNOME_VFS_URI_PATH_CHR == *cur */ {
887 /* ^[^:@/]+/.*$ */
889 if (uri_strlen_to (cur_tok_start, cur) > 0) {
890 *host_return = uri_strdup_to (cur_tok_start, cur);
893 cur_tok_start = cur;
896 if (*cur_tok_start != '\0' && uri_strlen_to (cur_tok_start, path_end) > 0) {
897 ret = uri_strdup_to(cur, path_end);
898 } else if (*host_return != NULL) {
899 ret = g_strdup (GNOME_VFS_URI_PATH_STR);
902 success = TRUE;
904 done:
905 if (*host_return != NULL) {
907 /* Check for an IPv6 address in square brackets.*/
908 if (strchr (*host_return, '[') && strchr (*host_return, ']') && strchr (*host_return, ':')) {
910 /* Extract the IPv6 address from square braced string. */
911 host = g_ascii_strdown ((*host_return) + 1, strlen (*host_return) - 2);
912 } else {
913 host = g_ascii_strdown (*host_return, -1);
916 g_free (*host_return);
917 *host_return = host;
921 /* If we didn't complete our mission, discard all the partials */
922 if (!success) {
923 g_free (*host_return);
924 g_free (*user_return);
925 g_free (*password_return);
926 g_free (ret);
928 *host_return = NULL;
929 *user_return = NULL;
930 *port_return = 0;
931 *password_return = NULL;
932 ret = NULL;
935 return ret;