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 */
23 static bool is_unsafe (unsigned char c
)
25 if (c
<= 0x1f || c
>= 0x7f)
60 originalString
= g_strdup("");
64 Uri::Uri (const Uri
& uri
)
73 g_datalist_init (¶ms
);
76 originalString
= NULL
;
79 Uri::Copy (&uri
, this);
90 g_free (scheme
); scheme
= NULL
;
91 g_free (user
); user
= NULL
;
92 g_free (auth
); auth
= NULL
;
93 g_free (passwd
); passwd
= NULL
;
94 g_free (host
); host
= NULL
;
95 g_free (path
); path
= NULL
;
96 g_datalist_clear (¶ms
);
97 g_datalist_init (¶ms
);
98 g_free (query
); query
= NULL
;
99 g_free (fragment
); fragment
= NULL
;
100 g_free (originalString
); originalString
= NULL
;
105 clone_params (GQuark quark
, gpointer data
, gpointer user_data
)
107 gchar
*str
= (gchar
*) data
;
108 GData
**params
= (GData
**) user_data
;
109 g_datalist_id_set_data_full (params
, quark
, g_strdup (str
), g_free
);
113 Uri::Copy (const Uri
*from
, Uri
*to
)
115 g_datalist_init (&to
->params
);
118 to
->scheme
= g_strdup (from
->scheme
);
119 to
->user
= g_strdup (from
->user
);
120 to
->auth
= g_strdup (from
->auth
);
121 to
->passwd
= g_strdup (from
->passwd
);
122 to
->host
= g_strdup (from
->host
);
123 to
->port
= from
->port
;
124 to
->path
= g_strdup (from
->path
);
125 g_datalist_foreach ((GData
**)&from
->params
, clone_params
, &to
->params
);
126 to
->query
= g_strdup (from
->query
);
127 to
->fragment
= g_strdup (from
->fragment
);
128 to
->originalString
= g_strdup (from
->originalString
);
129 to
->isAbsolute
= from
->isAbsolute
;
140 to
->originalString
= NULL
;
141 to
->isAbsolute
= false;
145 /* canonicalise a path */
147 canon_path (char *path
, bool allow_root
, bool allow_trailing_sep
)
149 register char *d
, *inptr
;
154 if (inptr
[0] == '/' && (inptr
[1] == '/' || (inptr
[1] == '\0' && !allow_trailing_sep
)))
160 if (!allow_root
&& (d
== path
+ 1) && d
[-1] == '/')
162 else if (allow_root
&& d
== path
&& path
[0] == '/')
167 return path
[0] ? path
: NULL
;
170 #define HEXVAL(c) (isdigit ((int) ((unsigned char) c)) ? (c) - '0' : tolower ((unsigned char) c) - 'a' + 10)
172 #define is_xdigit(c) isxdigit ((int) ((unsigned char) c))
175 url_decode (char *in
, const char *url
)
177 register char *inptr
, *outptr
;
182 if (is_xdigit (inptr
[1]) && is_xdigit (inptr
[2])) {
183 *outptr
++ = HEXVAL (inptr
[1]) * 16 + HEXVAL (inptr
[2]);
186 g_warning ("Invalid encoding in url: %s at %s", url
, inptr
);
187 *outptr
++ = *inptr
++;
190 *outptr
++ = *inptr
++;
197 Uri::Parse (const char *uri
, bool allow_trailing_sep
)
199 char *name
, *value
, *scheme
= NULL
, *user
= NULL
, *auth
= NULL
, *passwd
= NULL
, *host
= NULL
, *path
= NULL
, *query
= NULL
, *fragment
= NULL
;
200 register const char *start
, *inptr
;
202 bool parse_path
= false;
203 GData
*params
= NULL
;
217 while (*inptr
&& *inptr
!= ':' && *inptr
!= '/' && *inptr
!= '?' && *inptr
!= '#' && *inptr
!= '\\')
220 if (inptr
> start
&& *inptr
== ':') {
221 scheme
= g_ascii_strdown (start
, inptr
- start
);
229 if (!strncmp (inptr
, "//", 2))
233 while (*inptr
&& *inptr
!= ';' && *inptr
!= ':' && *inptr
!= '@' && *inptr
!= '/')
236 isAbsolute
= !strncmp (inptr
, "\\\\", 2);
243 case ';': /* <user>;auth= */
244 case ':': /* <user>:passwd or <host>:port */
245 case '@': /* <user>@host */
247 user
= g_strndup (start
, inptr
- start
);
248 url_decode (user
, uri
);
252 case ';': /* ;auth= */
253 if (!g_ascii_strncasecmp (inptr
, ";auth=", 6)) {
256 while (*inptr
&& *inptr
!= ':' && *inptr
!= '@')
260 auth
= g_strndup (start
, inptr
- start
);
261 url_decode (auth
, uri
);
268 } else if (*inptr
== '@') {
275 case ':': /* <user>:passwd@ or <host>:port */
279 while (*inptr
&& *inptr
!= '@' && *inptr
!= '/')
285 passwd
= g_strndup (start
, inptr
- start
);
286 url_decode (passwd
, uri
);
301 case '@': /* <user>@host */
305 while (*inptr
&& *inptr
!= ':' && *inptr
!= '/')
310 while (n
> 0 && start
[n
- 1] == '.')
314 host
= g_strndup (start
, n
);
322 while (*inptr
>= '0' && *inptr
<= '9' && port
< 6554)
323 port
= (port
* 10) + ((*inptr
++) - '0');
326 /* chop off the last digit */
330 while (*inptr
&& *inptr
!= '/')
335 case '/': /* <host>/path or simply <host> */
339 while (n
> 0 && start
[n
- 1] == '.')
343 host
= g_strndup (start
, n
);
350 if (parse_path
|| *inptr
== '/') {
351 /* look for params, query, or fragment */
353 while (*inptr
&& *inptr
!= ';' && *inptr
!= '?' && *inptr
!= '#')
356 /* canonicalise and save the path component */
357 if ((n
= (inptr
- start
))) {
358 value
= g_strndup (start
, n
);
359 url_decode (value
, uri
);
361 if (!(path
= canon_path (value
, !host
, allow_trailing_sep
)))
367 while (*inptr
== ';') {
368 while (*inptr
== ';')
372 while (*inptr
&& *inptr
!= '=' && *inptr
!= ';' && *inptr
!= '?' && *inptr
!= '#')
375 name
= g_strndup (start
, inptr
- start
);
376 url_decode (name
, uri
);
381 while (*inptr
&& *inptr
!= ';' && *inptr
!= '?' && *inptr
!= '#')
384 value
= g_strndup (start
, inptr
- start
);
385 url_decode (value
, uri
);
387 value
= g_strdup ("");
390 g_datalist_set_data_full (¶ms
, name
, value
, g_free
);
395 goto decode_fragment
;
396 else if (*inptr
!= '?')
403 while (*inptr
&& *inptr
!= '#')
406 query
= g_strndup (start
, inptr
- start
);
407 url_decode (query
, uri
);
415 fragment
= g_strdup (inptr
+ 1);
416 url_decode (fragment
, uri
);
427 this->scheme
= scheme
;
430 this->passwd
= passwd
;
434 this->params
= params
;
436 this->fragment
= fragment
;
437 this->originalString
= g_strdup (uri
);
438 this->isAbsolute
= isAbsolute
;
444 Uri::Combine (const char *relative_path
)
446 const char *filename
;
449 if (path
&& relative_path
[0] != '/') {
450 if (!(filename
= strrchr (path
, '/')))
451 new_path
= g_strdup (relative_path
);
453 new_path
= g_strdup_printf ("%.*s/%s", filename
- path
, path
, g_str_has_prefix (relative_path
, "../") ? relative_path
+3 : relative_path
);
460 path
= g_strdup (relative_path
);
465 Uri::Combine (const Uri
*relative_uri
)
467 if (relative_uri
->isAbsolute
)
468 g_warning ("Uri::Combine (): Not a relative Uri");
469 if (relative_uri
->path
)
470 Combine (relative_uri
->path
);
474 append_url_encoded (GString
*string
, const char *in
, const char *extra
)
476 register const char *inptr
= in
;
481 while (*inptr
&& !is_unsafe (*inptr
) && !strchr (extra
, *inptr
))
484 g_string_append_len (string
, start
, inptr
- start
);
486 while (*inptr
&& (is_unsafe (*inptr
) || strchr (extra
, *inptr
)))
487 g_string_append_printf (string
, "%%%.02hhx", *inptr
++);
492 append_param (GQuark key_id
, gpointer value
, gpointer user_data
)
494 GString
*string
= (GString
*) user_data
;
496 g_string_append_c (string
, ';');
497 append_url_encoded (string
, g_quark_to_string (key_id
), "?=#");
498 if (*((char *) value
)) {
499 g_string_append_c (string
, '=');
500 append_url_encoded (string
, (const char *) value
, "?;#");
505 Uri::ToString (UriToStringFlags flags
) const
510 string
= g_string_new ("");
513 g_string_append (string
, this->scheme
);
514 g_string_append (string
, "://");
517 append_url_encoded (string
, this->user
, ":;@/");
520 g_string_append (string
, ";auth=");
521 append_url_encoded (string
, this->auth
, ":@/");
524 if (this->passwd
&& !(flags
& UriHidePasswd
)) {
525 g_string_append_c (string
, ':');
526 append_url_encoded (string
, this->passwd
, "@/");
529 g_string_append_c (string
, '@');
532 g_string_append (string
, this->host
);
535 g_string_append_printf (string
, ":%d", this->port
);
539 if (this->host
&& *this->path
!= '/')
540 g_string_append_c (string
, '/');
542 append_url_encoded (string
, this->path
, ";?#");
543 } else if (this->host
&& (this->params
|| this->query
|| this->fragment
)) {
544 g_string_append_c (string
, '/');
548 g_datalist_foreach ((GData
**) &this->params
, append_param
, string
);
551 g_string_append_c (string
, '?');
552 append_url_encoded (string
, this->query
, "#");
555 if (this->fragment
&& !(flags
& UriHideFragment
)) {
556 g_string_append_c (string
, '#');
557 append_url_encoded (string
, this->fragment
, "");
561 g_string_free (string
, false);
567 Uri::operator== (const Uri
&v
) const
569 if (isAbsolute
!= v
.isAbsolute
)
573 if (!!scheme
!= !!v
.scheme
574 || (scheme
&& strcmp (scheme
, v
.scheme
)))
576 if (!!user
!= !!v
.user
577 || (user
&& strcmp (user
, v
.user
)))
579 if (!!auth
!= !!v
.auth
580 || (auth
&& strcmp (auth
, v
.auth
)))
582 if (!!passwd
!= !!v
.passwd
583 || (passwd
&& strcmp (passwd
, v
.passwd
)))
585 if (!!host
!= !!v
.host
586 || (host
&& strcmp (host
, v
.host
)))
588 if (!!path
!= !!v
.path
589 || (path
&& strcmp (path
, v
.path
)))
591 if (!!query
!= !!v
.query
592 || (query
&& strcmp (query
, v
.query
)))
594 if (!!fragment
!= !!v
.fragment
595 || (fragment
&& strcmp (fragment
, v
.fragment
)))
598 // XXX we don't compare this->data vs v.data yet.
600 // we intentionally don't compare original strings
605 Uri::Equals (const Uri
*left
, const Uri
*right
)
611 return left
->operator==(*right
);
615 Uri::IsNullOrEmpty (const Uri
*uri
)
617 if (!uri
|| (uri
->scheme
== NULL
&& uri
->user
== NULL
&& uri
->auth
== NULL
&&
618 uri
->passwd
== NULL
&& uri
->host
== NULL
&& uri
->port
== 0 && uri
->path
== NULL
619 && uri
->params
== NULL
&& uri
->query
== NULL
&& uri
->fragment
== NULL
&&
620 strcmp (uri
->originalString
, "") == 0 && !uri
->isAbsolute
))
629 char* str
= ToString();
630 guint hash
= g_str_hash (str
);
636 Uri::IsScheme (const char *scheme
)
638 if (!!this->scheme
!= !!scheme
)
642 return !g_ascii_strcasecmp (this->scheme
, scheme
);
648 Uri::SameSiteOfOrigin (const Uri
*left
, const Uri
*right
)
650 // works only on absolute URI
651 if (!left
|| !left
->isAbsolute
|| !right
|| !right
->isAbsolute
)
654 if (left
->port
!= right
->port
)
657 if (!left
->scheme
|| !right
->scheme
|| (strcmp (left
->scheme
, right
->scheme
) != 0))
660 // comparing 2 file:/// URI where no hosts is present
661 if (!left
->host
&& !right
->host
&& (strcmp (left
->scheme
, "file") == 0))
664 if (!left
->host
|| !right
->host
|| (strcmp (left
->host
, right
->host
) != 0))