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 #define HEXVAL(c) (isdigit ((int) ((unsigned char) c)) ? (c) - '0' : tolower ((unsigned char) c) - 'a' + 10)
224 #define is_xdigit(c) isxdigit ((int) ((unsigned char) c))
227 url_decode (char *in
, const char *url
)
229 register char *inptr
, *outptr
;
234 if (is_xdigit (inptr
[1]) && is_xdigit (inptr
[2])) {
235 *outptr
++ = HEXVAL (inptr
[1]) * 16 + HEXVAL (inptr
[2]);
238 g_warning ("Invalid encoding in url: %s at %s", url
, inptr
);
239 *outptr
++ = *inptr
++;
242 *outptr
++ = *inptr
++;
249 Uri::Parse (const char *uri
, bool allow_trailing_sep
)
251 char *name
, *value
, *scheme
= NULL
, *user
= NULL
, *auth
= NULL
, *passwd
= NULL
, *host
= NULL
, *path
= NULL
, *query
= NULL
, *fragment
= NULL
;
252 Uri::Param
*param
, *tail
, *params
= NULL
;
253 register const char *start
, *inptr
;
255 bool parse_path
= false;
259 tail
= (Uri::Param
*) ¶ms
;
270 while (*inptr
&& *inptr
!= ':' && *inptr
!= '/' && *inptr
!= '?' && *inptr
!= '#' && *inptr
!= '\\')
273 if (inptr
> start
&& *inptr
== ':') {
274 scheme
= g_ascii_strdown (start
, inptr
- start
);
282 if (!strncmp (inptr
, "//", 2))
286 while (*inptr
&& *inptr
!= ';' && *inptr
!= ':' && *inptr
!= '@' && *inptr
!= '/')
289 isAbsolute
= !strncmp (inptr
, "\\\\", 2);
296 case ';': /* <user>;auth= */
297 case ':': /* <user>:passwd or <host>:port */
298 case '@': /* <user>@host */
300 user
= g_strndup (start
, inptr
- start
);
301 url_decode (user
, uri
);
305 case ';': /* ;auth= */
306 if (!g_ascii_strncasecmp (inptr
, ";auth=", 6)) {
309 while (*inptr
&& *inptr
!= ':' && *inptr
!= '@')
313 auth
= g_strndup (start
, inptr
- start
);
314 url_decode (auth
, uri
);
321 } else if (*inptr
== '@') {
328 case ':': /* <user>:passwd@ or <host>:port */
332 while (*inptr
&& *inptr
!= '@' && *inptr
!= '/')
338 passwd
= g_strndup (start
, inptr
- start
);
339 url_decode (passwd
, uri
);
354 case '@': /* <user>@host */
358 while (*inptr
&& *inptr
!= ':' && *inptr
!= '/')
363 while (n
> 0 && start
[n
- 1] == '.')
367 host
= g_strndup (start
, n
);
375 while (*inptr
>= '0' && *inptr
<= '9' && port
< 6554)
376 port
= (port
* 10) + ((*inptr
++) - '0');
379 /* chop off the last digit */
383 while (*inptr
&& *inptr
!= '/')
388 case '/': /* <host>/path or simply <host> */
392 while (n
> 0 && start
[n
- 1] == '.')
396 host
= g_strndup (start
, n
);
403 if (parse_path
|| *inptr
== '/') {
404 /* look for params, query, or fragment */
406 while (*inptr
&& *inptr
!= ';' && *inptr
!= '?' && *inptr
!= '#')
409 /* canonicalise and save the path component */
410 if ((n
= (inptr
- start
))) {
411 value
= g_strndup (start
, n
);
412 url_decode (value
, uri
);
414 if (!(path
= canon_path (value
, !host
, allow_trailing_sep
)))
420 while (*inptr
== ';') {
421 while (*inptr
== ';')
425 while (*inptr
&& *inptr
!= '=' && *inptr
!= ';' && *inptr
!= '?' && *inptr
!= '#')
428 name
= g_strndup (start
, inptr
- start
);
429 url_decode (name
, uri
);
434 while (*inptr
&& *inptr
!= ';' && *inptr
!= '?' && *inptr
!= '#')
437 value
= g_strndup (start
, inptr
- start
);
438 url_decode (value
, uri
);
440 value
= g_strdup ("");
443 param
= g_new (Uri::Param
, 1);
444 param
->value
= value
;
453 goto decode_fragment
;
454 else if (*inptr
!= '?')
461 while (*inptr
&& *inptr
!= '#')
464 query
= g_strndup (start
, inptr
- start
);
465 url_decode (query
, uri
);
473 fragment
= g_strdup (inptr
+ 1);
474 url_decode (fragment
, uri
);
485 this->scheme
= scheme
;
488 this->passwd
= passwd
;
492 this->params
= params
;
494 this->fragment
= fragment
;
495 this->originalString
= g_strdup (uri
);
496 this->isAbsolute
= isAbsolute
;
502 Uri::Combine (const char *relative_path
)
504 const char *filename
;
507 if (path
&& relative_path
[0] != '/') {
508 if (!(filename
= strrchr (path
, '/')))
509 new_path
= g_strdup (relative_path
);
511 new_path
= g_strdup_printf ("%.*s/%s", filename
- path
, path
, g_str_has_prefix (relative_path
, "../") ? relative_path
+3 : relative_path
);
518 path
= g_strdup (relative_path
);
523 Uri::Combine (const Uri
*relative_uri
)
525 if (relative_uri
->isAbsolute
)
526 g_warning ("Uri::Combine (): Not a relative Uri");
527 if (relative_uri
->path
)
528 Combine (relative_uri
->path
);
532 append_url_encoded (GString
*string
, const char *in
, const char *extra
)
534 register const char *inptr
= in
;
539 while (*inptr
&& !is_unsafe (*inptr
) && !strchr (extra
, *inptr
))
542 g_string_append_len (string
, start
, inptr
- start
);
544 while (*inptr
&& (is_unsafe (*inptr
) || strchr (extra
, *inptr
)))
545 g_string_append_printf (string
, "%%%.02hhx", *inptr
++);
550 append_param (GString
*string
, Uri::Param
*param
)
552 g_string_append_c (string
, ';');
554 append_url_encoded (string
, param
->name
, "?=#");
556 if (param
->value
&& *param
->value
) {
557 g_string_append_c (string
, '=');
558 append_url_encoded (string
, param
->value
, "?;#");
563 Uri::ToString (UriToStringFlags flags
) const
569 string
= g_string_new ("");
572 g_string_append (string
, this->scheme
);
573 g_string_append (string
, "://");
576 append_url_encoded (string
, this->user
, ":;@/");
579 g_string_append (string
, ";auth=");
580 append_url_encoded (string
, this->auth
, ":@/");
583 if (this->passwd
&& !(flags
& UriHidePasswd
)) {
584 g_string_append_c (string
, ':');
585 append_url_encoded (string
, this->passwd
, "@/");
588 g_string_append_c (string
, '@');
591 g_string_append (string
, this->host
);
594 g_string_append_printf (string
, ":%d", this->port
);
598 if (this->host
&& *this->path
!= '/')
599 g_string_append_c (string
, '/');
601 append_url_encoded (string
, this->path
, ";?#");
602 } else if (this->host
&& (this->params
|| this->query
|| this->fragment
)) {
603 g_string_append_c (string
, '/');
606 param
= this->params
;
608 append_param (string
, param
);
612 if (this->query
&& !(flags
& UriHideQuery
)) {
613 g_string_append_c (string
, '?');
614 append_url_encoded (string
, this->query
, "#");
617 if (this->fragment
&& !(flags
& UriHideFragment
)) {
618 g_string_append_c (string
, '#');
619 append_url_encoded (string
, this->fragment
, "");
623 g_string_free (string
, false);
629 Uri::operator== (const Uri
&v
) const
631 if (isAbsolute
!= v
.isAbsolute
)
635 if (!!scheme
!= !!v
.scheme
636 || (scheme
&& strcmp (scheme
, v
.scheme
)))
638 if (!!user
!= !!v
.user
639 || (user
&& strcmp (user
, v
.user
)))
641 if (!!auth
!= !!v
.auth
642 || (auth
&& strcmp (auth
, v
.auth
)))
644 if (!!passwd
!= !!v
.passwd
645 || (passwd
&& strcmp (passwd
, v
.passwd
)))
647 if (!!host
!= !!v
.host
648 || (host
&& strcmp (host
, v
.host
)))
650 if (!!path
!= !!v
.path
651 || (path
&& strcmp (path
, v
.path
)))
653 if (!!query
!= !!v
.query
654 || (query
&& strcmp (query
, v
.query
)))
656 if (!!fragment
!= !!v
.fragment
657 || (fragment
&& strcmp (fragment
, v
.fragment
)))
659 if (!uri_params_equal (params
, v
.params
))
662 // we intentionally don't compare original strings
667 Uri::Equals (const Uri
*left
, const Uri
*right
)
673 return left
->operator==(*right
);
677 Uri::IsNullOrEmpty (const Uri
*uri
)
679 if (!uri
|| (uri
->scheme
== NULL
&& uri
->user
== NULL
&& uri
->auth
== NULL
&&
680 uri
->passwd
== NULL
&& uri
->host
== NULL
&& uri
->port
== 0 && uri
->path
== NULL
681 && uri
->params
== NULL
&& uri
->query
== NULL
&& uri
->fragment
== NULL
&&
682 strcmp (uri
->originalString
, "") == 0 && !uri
->isAbsolute
))
691 char* str
= ToString();
692 guint hash
= g_str_hash (str
);
698 Uri::IsScheme (const char *scheme
) const
700 if (!!this->scheme
!= !!scheme
)
704 return !g_ascii_strcasecmp (this->scheme
, scheme
);
710 Uri::SameSiteOfOrigin (const Uri
*left
, const Uri
*right
)
712 // works only on absolute URI
713 if (!left
|| !left
->isAbsolute
|| !right
|| !right
->isAbsolute
)
716 if (left
->port
!= right
->port
)
719 if (!left
->scheme
|| !right
->scheme
|| (strcmp (left
->scheme
, right
->scheme
) != 0))
722 // comparing 2 file:/// URI where no hosts is present
723 if (!left
->host
&& !right
->host
&& (strcmp (left
->scheme
, "file") == 0))
726 if (!left
->host
|| !right
->host
|| (strcmp (left
->host
, right
->host
) != 0))