1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
22 /* see rfc1738, section 2.2 */
24 is_unsafe (unsigned char c
)
26 if (c
<= 0x1f || c
>= 0x7f)
50 uri_params_clear (Uri::Param
**params
)
52 Uri::Param
*next
, *param
= *params
;
56 g_free (param
->value
);
66 uri_params_copy (Uri::Param
*params
)
68 Uri::Param
*list
, *param
, *cur
, *tail
;
71 tail
= (Uri::Param
*) &list
;
75 param
= g_new (Uri::Param
, 1);
76 param
->value
= g_strdup (cur
->value
);
77 param
->name
= g_strdup (cur
->name
);
90 uri_params_equal (Uri::Param
*params0
, Uri::Param
*params1
)
92 // Note: this might need to be changed to allow params out of order
93 Uri::Param
*p0
= params0
;
94 Uri::Param
*p1
= params1
;
97 if (!p1
|| strcmp (p0
->name
, p1
->name
) != 0
98 || strcmp (p0
->value
, p1
->value
) != 0)
123 originalString
= g_strdup("");
127 Uri::Uri (const Uri
& uri
)
139 originalString
= NULL
;
142 Uri::Copy (&uri
, this);
153 g_free (scheme
); scheme
= NULL
;
154 g_free (user
); user
= NULL
;
155 g_free (auth
); auth
= NULL
;
156 g_free (passwd
); passwd
= NULL
;
157 g_free (host
); host
= NULL
;
158 g_free (path
); path
= NULL
;
159 uri_params_clear (¶ms
);
160 g_free (query
); query
= NULL
;
161 g_free (fragment
); fragment
= NULL
;
162 g_free (originalString
); originalString
= NULL
;
167 Uri::Copy (const Uri
*from
, Uri
*to
)
170 to
->scheme
= g_strdup (from
->scheme
);
171 to
->user
= g_strdup (from
->user
);
172 to
->auth
= g_strdup (from
->auth
);
173 to
->passwd
= g_strdup (from
->passwd
);
174 to
->host
= g_strdup (from
->host
);
175 to
->port
= from
->port
;
176 to
->path
= g_strdup (from
->path
);
177 to
->params
= uri_params_copy (from
->params
);
178 to
->query
= g_strdup (from
->query
);
179 to
->fragment
= g_strdup (from
->fragment
);
180 to
->originalString
= g_strdup (from
->originalString
);
181 to
->isAbsolute
= from
->isAbsolute
;
192 to
->originalString
= NULL
;
193 to
->isAbsolute
= false;
197 /* canonicalise a path */
199 canon_path (char *path
, bool allow_root
, bool allow_trailing_sep
)
201 register char *d
, *inptr
;
206 if (inptr
[0] == '/' && (inptr
[1] == '/' || (inptr
[1] == '\0' && !allow_trailing_sep
)))
212 if (!allow_root
&& (d
== path
+ 1) && d
[-1] == '/')
214 else if (allow_root
&& d
== path
&& path
[0] == '/')
219 return path
[0] ? path
: NULL
;
222 struct path_component_t
{
228 flatten_path (const char *path
)
230 path_component_t part
;
241 parts
= g_array_new (false, false, sizeof (path_component_t
));
246 while (*inptr
== '/')
253 while (*inptr
&& *inptr
!= '/')
256 part
.len
= (size_t) (inptr
- part
.start
);
258 if (part
.len
== 2 && !strncmp (part
.start
, "..", 2)) {
259 // drop the most recent parent (if not ..)
260 if (parts
->len
> 0) {
261 path_component_t prev_part
= g_array_index (parts
, path_component_t
, parts
->len
- 1);
262 if (prev_part
.len
== 2 && !strncmp (prev_part
.start
, "..", 2)) {
272 } else if (part
.len
== 1 && !strncmp (part
.start
, ".", 1)) {
273 // drop this path component
275 } else if (part
.len
> 0) {
276 // keep track of this component
281 g_array_append_val (parts
, part
);
286 // at this point, n is the char count of all path components (minus separators)
288 p
= result
= (char *) g_malloc (n
+ 2);
293 for (i
= 0; i
< parts
->len
; i
++) {
294 part
= g_array_index (parts
, path_component_t
, i
);
295 memcpy (p
, part
.start
, part
.len
);
301 if (p
> result
&& inptr
> path
&& inptr
[-1] != '/')
304 g_array_free (parts
, true);
309 #define HEXVAL(c) (isdigit ((int) ((unsigned char) c)) ? (c) - '0' : tolower ((unsigned char) c) - 'a' + 10)
311 #define is_xdigit(c) isxdigit ((int) ((unsigned char) c))
314 url_decode (char *in
, const char *url
)
316 register char *inptr
, *outptr
;
321 if (is_xdigit (inptr
[1]) && is_xdigit (inptr
[2])) {
322 *outptr
++ = HEXVAL (inptr
[1]) * 16 + HEXVAL (inptr
[2]);
325 g_warning ("Invalid encoding in url: %s at %s", url
, inptr
);
326 *outptr
++ = *inptr
++;
329 *outptr
++ = *inptr
++;
336 Uri::Parse (const char *uri
, bool allow_trailing_sep
)
338 char *name
, *value
, *scheme
= NULL
, *user
= NULL
, *auth
= NULL
, *passwd
= NULL
, *host
= NULL
, *path
= NULL
, *query
= NULL
, *fragment
= NULL
;
339 Uri::Param
*param
, *tail
, *params
= NULL
;
340 register const char *start
, *inptr
;
342 bool parse_path
= false;
346 tail
= (Uri::Param
*) ¶ms
;
357 while (*inptr
&& *inptr
!= ':' && *inptr
!= '/' && *inptr
!= '?' && *inptr
!= '#' && *inptr
!= '\\')
360 if (inptr
> start
&& *inptr
== ':') {
361 scheme
= g_ascii_strdown (start
, inptr
- start
);
369 if (!strncmp (inptr
, "//", 2))
373 while (*inptr
&& *inptr
!= ';' && *inptr
!= ':' && *inptr
!= '@' && *inptr
!= '/')
376 isAbsolute
= !strncmp (inptr
, "\\\\", 2);
383 case ';': /* <user>;auth= */
384 case ':': /* <user>:passwd or <host>:port */
385 case '@': /* <user>@host */
387 user
= g_strndup (start
, inptr
- start
);
388 url_decode (user
, uri
);
392 case ';': /* ;auth= */
393 if (!g_ascii_strncasecmp (inptr
, ";auth=", 6)) {
396 while (*inptr
&& *inptr
!= ':' && *inptr
!= '@')
400 auth
= g_strndup (start
, inptr
- start
);
401 url_decode (auth
, uri
);
408 } else if (*inptr
== '@') {
415 case ':': /* <user>:passwd@ or <host>:port */
419 while (*inptr
&& *inptr
!= '@' && *inptr
!= '/')
425 passwd
= g_strndup (start
, inptr
- start
);
426 url_decode (passwd
, uri
);
441 case '@': /* <user>@host */
445 while (*inptr
&& *inptr
!= ':' && *inptr
!= '/')
450 while (n
> 0 && start
[n
- 1] == '.')
454 host
= g_strndup (start
, n
);
462 while (*inptr
>= '0' && *inptr
<= '9' && port
< 6554)
463 port
= (port
* 10) + ((*inptr
++) - '0');
466 /* chop off the last digit */
470 while (*inptr
&& *inptr
!= '/')
475 case '/': /* <host>/path or simply <host> */
479 while (n
> 0 && start
[n
- 1] == '.')
483 host
= g_strndup (start
, n
);
490 if (parse_path
|| *inptr
== '/') {
491 /* look for params, query, or fragment */
493 while (*inptr
&& *inptr
!= ';' && *inptr
!= '?' && *inptr
!= '#')
496 /* canonicalise and save the path component */
497 if ((n
= (inptr
- start
))) {
498 value
= g_strndup (start
, n
);
499 url_decode (value
, uri
);
501 if (!(path
= canon_path (value
, !host
, allow_trailing_sep
)))
505 value
= flatten_path (path
);
513 while (*inptr
== ';') {
514 while (*inptr
== ';')
518 while (*inptr
&& *inptr
!= '=' && *inptr
!= ';' && *inptr
!= '?' && *inptr
!= '#')
521 name
= g_strndup (start
, inptr
- start
);
522 url_decode (name
, uri
);
527 while (*inptr
&& *inptr
!= ';' && *inptr
!= '?' && *inptr
!= '#')
530 value
= g_strndup (start
, inptr
- start
);
531 url_decode (value
, uri
);
533 value
= g_strdup ("");
536 param
= g_new (Uri::Param
, 1);
537 param
->value
= value
;
546 goto decode_fragment
;
547 else if (*inptr
!= '?')
554 while (*inptr
&& *inptr
!= '#')
557 query
= g_strndup (start
, inptr
- start
);
558 url_decode (query
, uri
);
566 fragment
= g_strdup (inptr
+ 1);
567 url_decode (fragment
, uri
);
578 this->scheme
= scheme
;
581 this->passwd
= passwd
;
585 this->params
= params
;
587 this->fragment
= fragment
;
588 this->originalString
= g_strdup (uri
);
589 this->isAbsolute
= isAbsolute
;
595 Uri::Combine (const char *relative_path
)
599 // strip off the 'filename' component
600 if (!(p
= strrchr (path
, '/')))
604 // combine with the relative path
605 combined
= g_strdup_printf ("%s/%s", path
, relative_path
);
608 // flatten the resulting combined path
609 path
= flatten_path (combined
);
612 path
= flatten_path (relative_path
);
617 Uri::Combine (const Uri
*relative_uri
)
619 if (relative_uri
->isAbsolute
)
620 g_warning ("Uri::Combine (): Not a relative Uri");
621 if (relative_uri
->path
)
622 Combine (relative_uri
->path
);
626 append_url_encoded (GString
*string
, const char *in
, const char *extra
)
628 register const char *inptr
= in
;
633 while (*inptr
&& !is_unsafe (*inptr
) && !strchr (extra
, *inptr
))
636 g_string_append_len (string
, start
, inptr
- start
);
638 while (*inptr
&& (is_unsafe (*inptr
) || strchr (extra
, *inptr
)))
639 g_string_append_printf (string
, "%%%.02hhx", *inptr
++);
644 append_param (GString
*string
, Uri::Param
*param
)
646 g_string_append_c (string
, ';');
648 append_url_encoded (string
, param
->name
, "?=#");
650 if (param
->value
&& *param
->value
) {
651 g_string_append_c (string
, '=');
652 append_url_encoded (string
, param
->value
, "?;#");
657 Uri::ToString (UriToStringFlags flags
) const
663 string
= g_string_new ("");
666 g_string_append (string
, this->scheme
);
667 g_string_append (string
, "://");
670 append_url_encoded (string
, this->user
, ":;@/");
673 g_string_append (string
, ";auth=");
674 append_url_encoded (string
, this->auth
, ":@/");
677 if (this->passwd
&& !(flags
& UriHidePasswd
)) {
678 g_string_append_c (string
, ':');
679 append_url_encoded (string
, this->passwd
, "@/");
682 g_string_append_c (string
, '@');
685 g_string_append (string
, this->host
);
688 g_string_append_printf (string
, ":%d", this->port
);
692 if (this->host
&& *this->path
!= '/')
693 g_string_append_c (string
, '/');
695 append_url_encoded (string
, this->path
, " ;?#");
696 } else if (this->host
&& (this->params
|| this->query
|| this->fragment
)) {
697 g_string_append_c (string
, '/');
700 param
= this->params
;
702 append_param (string
, param
);
706 if (this->query
&& !(flags
& UriHideQuery
)) {
707 g_string_append_c (string
, '?');
708 append_url_encoded (string
, this->query
, "#");
711 if (this->fragment
&& !(flags
& UriHideFragment
)) {
712 g_string_append_c (string
, '#');
713 append_url_encoded (string
, this->fragment
, "");
717 g_string_free (string
, false);
723 Uri::operator== (const Uri
&v
) const
725 if (isAbsolute
!= v
.isAbsolute
)
729 if (!!scheme
!= !!v
.scheme
730 || (scheme
&& strcmp (scheme
, v
.scheme
)))
732 if (!!user
!= !!v
.user
733 || (user
&& strcmp (user
, v
.user
)))
735 if (!!auth
!= !!v
.auth
736 || (auth
&& strcmp (auth
, v
.auth
)))
738 if (!!passwd
!= !!v
.passwd
739 || (passwd
&& strcmp (passwd
, v
.passwd
)))
741 if (!!host
!= !!v
.host
742 || (host
&& strcmp (host
, v
.host
)))
744 if (!!path
!= !!v
.path
745 || (path
&& strcmp (path
, v
.path
)))
747 if (!!query
!= !!v
.query
748 || (query
&& strcmp (query
, v
.query
)))
750 if (!!fragment
!= !!v
.fragment
751 || (fragment
&& strcmp (fragment
, v
.fragment
)))
753 if (!uri_params_equal (params
, v
.params
))
756 // we intentionally don't compare original strings
761 Uri::Equals (const Uri
*left
, const Uri
*right
)
767 return left
->operator==(*right
);
771 Uri::IsNullOrEmpty (const Uri
*uri
)
773 if (!uri
|| (uri
->scheme
== NULL
&& uri
->user
== NULL
&& uri
->auth
== NULL
&&
774 uri
->passwd
== NULL
&& uri
->host
== NULL
&& uri
->port
== 0 && uri
->path
== NULL
775 && uri
->params
== NULL
&& uri
->query
== NULL
&& uri
->fragment
== NULL
&&
776 strcmp (uri
->originalString
, "") == 0 && !uri
->isAbsolute
))
785 char* str
= ToString();
786 guint hash
= g_str_hash (str
);
792 Uri::IsScheme (const char *scheme
) const
794 if (!!this->scheme
!= !!scheme
)
798 return !g_ascii_strcasecmp (this->scheme
, scheme
);
804 Uri::SameSiteOfOrigin (const Uri
*left
, const Uri
*right
)
806 // works only on absolute URI
807 if (!left
|| !left
->isAbsolute
|| !right
|| !right
->isAbsolute
)
810 if (left
->port
!= right
->port
)
813 if (!left
->scheme
|| !right
->scheme
|| (strcmp (left
->scheme
, right
->scheme
) != 0))
816 // comparing 2 file:/// URI where no hosts is present
817 if (!left
->host
&& !right
->host
&& (strcmp (left
->scheme
, "file") == 0))
820 if (!left
->host
|| !right
->host
|| (strcmp (left
->host
, right
->host
) != 0))