]>
git.saurik.com Git - wxWidgets.git/blob - src/common/uri.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/uri.cpp
3 // Purpose: Implementation of a URI parser
4 // Author: Ryan Norton,
5 // Vadim Zeitlin (UTF-8 URI support, many other changes)
8 // Copyright: (c) 2004 Ryan Norton,
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
13 // ===========================================================================
15 // ===========================================================================
17 // ---------------------------------------------------------------------------
19 // ---------------------------------------------------------------------------
21 // For compilers that support precompilation, includes "wx.h".
22 #include "wx/wxprec.h"
34 // ---------------------------------------------------------------------------
36 // ---------------------------------------------------------------------------
38 IMPLEMENT_CLASS(wxURI
, wxObject
)
40 // ===========================================================================
41 // wxURI implementation
42 // ===========================================================================
44 // ---------------------------------------------------------------------------
45 // Constructors and cleanup
46 // ---------------------------------------------------------------------------
49 : m_hostType(wxURI_REGNAME
),
54 wxURI::wxURI(const wxString
& uri
)
55 : m_hostType(wxURI_REGNAME
),
61 bool wxURI::Create(const wxString
& uri
)
66 return Parse(uri
.utf8_str());
77 m_fragment
= wxEmptyString
;
79 m_hostType
= wxURI_REGNAME
;
84 // ---------------------------------------------------------------------------
85 // Escaped characters handling
86 // ---------------------------------------------------------------------------
88 // Converts a character into a numeric hexadecimal value, or -1 if the passed
89 // in character is not a valid hex character
92 int wxURI::CharToHex(char c
)
94 if ((c
>= 'A') && (c
<= 'Z'))
96 if ((c
>= 'a') && (c
<= 'z'))
98 if ((c
>= '0') && (c
<= '9'))
104 int wxURI::DecodeEscape(wxString::const_iterator
& i
)
106 int hi
= CharToHex(*++i
);
110 int lo
= CharToHex(*++i
);
114 return (hi
<< 4) | lo
;
118 wxString
wxURI::Unescape(const wxString
& uri
)
120 // the unescaped version can't be longer than the original one
121 wxCharBuffer
buf(uri
.length());
122 char *p
= buf
.data();
124 for ( wxString::const_iterator i
= uri
.begin(); i
!= uri
.end(); ++i
, ++p
)
129 int n
= wxURI::DecodeEscape(i
);
133 wxASSERT_MSG( n
>= 0 && n
<= 0xff, "unexpected character value" );
135 c
= static_cast<char>(n
);
143 // by default assume that the URI is in UTF-8, this is the most common
145 wxString s
= wxString::FromUTF8(buf
);
148 // if it isn't, use latin-1 as a fallback -- at least this always
150 s
= wxCSConv(wxFONTENCODING_ISO8859_1
).cMB2WC(buf
);
156 void wxURI::AppendNextEscaped(wxString
& s
, const char *& p
)
158 // check for an already encoded character:
160 // pct-encoded = "%" HEXDIG HEXDIG
161 if ( p
[0] == '%' && IsHex(p
[1]) && IsHex(p
[2]) )
167 else // really needs escaping
169 static const char* hexDigits
= "0123456789abcdef";
174 s
+= hexDigits
[(c
>> 4) & 15];
175 s
+= hexDigits
[c
& 15];
179 // ---------------------------------------------------------------------------
183 // Gets the username and password via the old URL method.
184 // ---------------------------------------------------------------------------
185 wxString
wxURI::GetUser() const
187 // if there is no colon at all, find() returns npos and this method returns
188 // the entire string which is correct as it means that password was omitted
189 return m_userinfo(0, m_userinfo
.find(':'));
192 wxString
wxURI::GetPassword() const
194 size_t posColon
= m_userinfo
.find(':');
196 if ( posColon
== wxString::npos
)
199 return m_userinfo(posColon
+ 1, wxString::npos
);
202 // combine all URI fields in a single string, applying funcDecode to each
203 // component which it may make sense to decode (i.e. "unescape")
204 wxString
wxURI::DoBuildURI(wxString (*funcDecode
)(const wxString
&)) const
209 ret
+= m_scheme
+ ":";
216 ret
+= funcDecode(m_userinfo
) + "@";
218 if (m_hostType
== wxURI_REGNAME
)
219 ret
+= funcDecode(m_server
);
227 ret
+= funcDecode(m_path
);
230 ret
+= "?" + funcDecode(m_query
);
233 ret
+= "#" + funcDecode(m_fragment
);
238 // ---------------------------------------------------------------------------
240 // ---------------------------------------------------------------------------
242 bool wxURI::operator==(const wxURI
& uri
) const
246 if(m_scheme
!= uri
.m_scheme
)
249 else if (uri
.HasScheme())
257 if (m_userinfo
!= uri
.m_userinfo
)
260 else if (uri
.HasUserInfo())
263 if (m_server
!= uri
.m_server
||
264 m_hostType
!= uri
.m_hostType
)
269 if(m_port
!= uri
.m_port
)
272 else if (uri
.HasPort())
275 else if (uri
.HasServer())
281 if(m_path
!= uri
.m_path
)
284 else if (uri
.HasPath())
289 if (m_query
!= uri
.m_query
)
292 else if (uri
.HasQuery())
297 if (m_fragment
!= uri
.m_fragment
)
300 else if (uri
.HasFragment())
306 // ---------------------------------------------------------------------------
309 // if there is no authority or scheme, it is a reference
310 // ---------------------------------------------------------------------------
312 bool wxURI::IsReference() const
314 return !HasScheme() || !HasServer();
317 // ---------------------------------------------------------------------------
320 // Master URI parsing method. Just calls the individual parsing methods
322 // URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
323 // URI-reference = URI / relative
324 // ---------------------------------------------------------------------------
326 bool wxURI::Parse(const char *uri
)
328 uri
= ParseScheme(uri
);
330 uri
= ParseAuthority(uri
);
332 uri
= ParsePath(uri
);
334 uri
= ParseQuery(uri
);
336 uri
= ParseFragment(uri
);
338 // we only succeed if we parsed the entire string
339 return uri
&& *uri
== '\0';
342 const char* wxURI::ParseScheme(const char *uri
)
344 const char * const start
= uri
;
346 // assume that we have a scheme if we have the valid start of it
351 //scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
352 while (IsAlpha(*uri
) || IsDigit(*uri
) ||
363 //mark the scheme as valid
364 m_fields
|= wxURI_SCHEME
;
366 //move reference point up to input buffer
369 else // no valid scheme finally
371 uri
= start
; // rewind
375 //else: can't have schema, possible a relative URI
380 const char* wxURI::ParseAuthority(const char* uri
)
382 // authority = [ userinfo "@" ] host [ ":" port ]
383 if ( uri
[0] == '/' && uri
[1] == '/' )
385 //skip past the two slashes
388 // ############# DEVIATION FROM RFC #########################
389 // Don't parse the server component for file URIs
390 if(m_scheme
!= "file")
393 uri
= ParseUserInfo(uri
);
394 uri
= ParseServer(uri
);
395 return ParsePort(uri
);
402 const char* wxURI::ParseUserInfo(const char* uri
)
404 const char * const start
= uri
;
406 // userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
407 while ( *uri
&& *uri
!= '@' && *uri
!= '/' && *uri
!= '#' && *uri
!= '?' )
409 if ( IsUnreserved(*uri
) || IsSubDelim(*uri
) || *uri
== ':' )
410 m_userinfo
+= *uri
++;
412 AppendNextEscaped(m_userinfo
, uri
);
418 m_fields
|= wxURI_USERINFO
;
422 uri
= start
; // rewind
429 const char* wxURI::ParseServer(const char* uri
)
431 const char * const start
= uri
;
433 // host = IP-literal / IPv4address / reg-name
434 // IP-literal = "[" ( IPv6address / IPvFuture ) "]"
438 if (ParseIPv6address(uri
) && *uri
== ']')
440 m_hostType
= wxURI_IPV6ADDRESS
;
442 m_server
.assign(start
+ 1, uri
- start
- 1);
447 uri
= start
+ 1; // skip the leading '[' again
449 if (ParseIPvFuture(uri
) && *uri
== ']')
451 m_hostType
= wxURI_IPVFUTURE
;
453 m_server
.assign(start
+ 1, uri
- start
- 1);
456 else // unrecognized IP literal
462 else // IPv4 or a reg-name
464 if (ParseIPv4address(uri
))
466 m_hostType
= wxURI_IPV4ADDRESS
;
468 m_server
.assign(start
, uri
- start
);
476 if ( m_hostType
== wxURI_REGNAME
)
479 // reg-name = *( unreserved / pct-encoded / sub-delims )
480 while ( *uri
&& *uri
!= '/' && *uri
!= ':' && *uri
!= '#' && *uri
!= '?' )
482 if ( IsUnreserved(*uri
) || IsSubDelim(*uri
) )
485 AppendNextEscaped(m_server
, uri
);
489 m_fields
|= wxURI_SERVER
;
495 const char* wxURI::ParsePort(const char* uri
)
501 while ( IsDigit(*uri
) )
506 m_fields
|= wxURI_PORT
;
512 const char* wxURI::ParsePath(const char* uri
)
514 /// hier-part = "//" authority path-abempty
519 /// relative-part = "//" authority path-abempty
524 /// path-abempty = *( "/" segment )
525 /// path-absolute = "/" [ segment-nz *( "/" segment ) ]
526 /// path-noscheme = segment-nz-nc *( "/" segment )
527 /// path-rootless = segment-nz *( "/" segment )
528 /// path-empty = 0<pchar>
531 /// segment-nz = 1*pchar
532 /// segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
533 /// ; non-zero-length segment without any colon ":"
535 /// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
537 if ( IsEndPath(*uri
) )
540 const bool isAbs
= *uri
== '/';
544 wxArrayString segments
;
548 const bool endPath
= IsEndPath(*uri
);
549 if ( endPath
|| *uri
== '/' )
551 // end of a segment, look at what we got
552 if ( segment
== ".." )
554 if ( !segments
.empty() && *segments
.rbegin() != ".." )
557 segments
.push_back("..");
559 else if ( segment
== "." )
561 // normally we ignore "." but the last one should be taken into
562 // account as "path/." is the same as "path/" and not just "path"
564 segments
.push_back("");
566 else // normal segment
568 segments
.push_back(segment
);
579 if ( IsUnreserved(*uri
) || IsSubDelim(*uri
) || *uri
== ':' || *uri
== '@' )
582 AppendNextEscaped(segment
, uri
);
585 m_path
+= wxJoin(segments
, '/', '\0');
586 m_fields
|= wxURI_PATH
;
592 const char* wxURI::ParseQuery(const char* uri
)
594 // query = *( pchar / "/" / "?" )
598 while ( *uri
&& *uri
!= '#' )
600 if ( IsUnreserved(*uri
) || IsSubDelim(*uri
) ||
601 *uri
== ':' || *uri
== '@' || *uri
== '/' || *uri
== '?' )
604 AppendNextEscaped(m_query
, uri
);
607 m_fields
|= wxURI_QUERY
;
614 const char* wxURI::ParseFragment(const char* uri
)
616 // fragment = *( pchar / "/" / "?" )
622 if ( IsUnreserved(*uri
) || IsSubDelim(*uri
) ||
623 *uri
== ':' || *uri
== '@' || *uri
== '/' || *uri
== '?')
624 m_fragment
+= *uri
++;
626 AppendNextEscaped(m_fragment
, uri
);
629 m_fields
|= wxURI_FRAGMENT
;
635 // ---------------------------------------------------------------------------
638 // Builds missing components of this uri from a base uri
640 // A version of the algorithm outlined in the RFC is used here
641 // (it is shown in comments)
643 // Note that an empty URI inherits all components
644 // ---------------------------------------------------------------------------
647 wxArrayString
wxURI::SplitInSegments(const wxString
& path
)
649 return wxSplit(path
, '/', '\0' /* no escape character */);
652 void wxURI::Resolve(const wxURI
& base
, int flags
)
654 wxASSERT_MSG(!base
.IsReference(),
655 "wxURI to inherit from must not be a reference!");
657 // If we aren't being strict, enable the older (pre-RFC2396) loophole that
658 // allows this uri to inherit other properties from the base uri - even if
659 // the scheme is defined
660 if ( !(flags
& wxURI_STRICT
) &&
661 HasScheme() && base
.HasScheme() &&
662 m_scheme
== base
.m_scheme
)
664 m_fields
-= wxURI_SCHEME
;
668 // Do nothing if this is an absolute wxURI
669 // if defined(R.scheme) then
670 // T.scheme = R.scheme;
671 // T.authority = R.authority;
672 // T.path = remove_dot_segments(R.path);
673 // T.query = R.query;
677 //No scheme - inherit
678 m_scheme
= base
.m_scheme
;
679 m_fields
|= wxURI_SCHEME
;
681 // All we need to do for relative URIs with an
682 // authority component is just inherit the scheme
683 // if defined(R.authority) then
684 // T.authority = R.authority;
685 // T.path = remove_dot_segments(R.path);
686 // T.query = R.query;
690 //No authority - inherit
691 if (base
.HasUserInfo())
693 m_userinfo
= base
.m_userinfo
;
694 m_fields
|= wxURI_USERINFO
;
697 m_server
= base
.m_server
;
698 m_hostType
= base
.m_hostType
;
699 m_fields
|= wxURI_SERVER
;
703 m_port
= base
.m_port
;
704 m_fields
|= wxURI_PORT
;
708 // Simple path inheritance from base
711 // T.path = Base.path;
712 m_path
= base
.m_path
;
713 m_fields
|= wxURI_PATH
;
716 // if defined(R.query) then
717 // T.query = R.query;
719 // T.query = Base.query;
723 m_query
= base
.m_query
;
724 m_fields
|= wxURI_QUERY
;
727 else if ( m_path
.empty() || m_path
[0u] != '/' )
729 // if (R.path starts-with "/") then
730 // T.path = remove_dot_segments(R.path);
732 // T.path = merge(Base.path, R.path);
733 // T.path = remove_dot_segments(T.path);
735 // T.query = R.query;
737 // So we don't do anything for absolute paths and implement merge for
740 wxArrayString
our(SplitInSegments(m_path
)),
741 result(SplitInSegments(base
.m_path
));
743 if ( !result
.empty() )
748 // if we have an empty path it means we were constructed from a "."
749 // string or something similar (e.g. "././././"), it should count
750 // as (empty) segment
754 const wxArrayString::const_iterator end
= our
.end();
755 for ( wxArrayString::const_iterator i
= our
.begin(); i
!= end
; ++i
)
757 if ( i
->empty() || *i
== "." )
759 // as in ParsePath(), while normally we ignore the empty
760 // segments, we need to take account of them at the end
762 result
.push_back("");
768 if ( !result
.empty() )
773 result
.push_back("");
775 //else: just ignore, extra ".." don't accumulate
779 if ( result
.empty() )
781 // ensure that the resulting path will always be absolute
782 result
.push_back("");
785 result
.push_back(*i
);
789 m_path
= wxJoin(result
, '/', '\0');
792 //T.fragment = R.fragment;
795 // ---------------------------------------------------------------------------
798 // Parses 1 to 4 hex values. Returns true if the first character of the input
799 // string is a valid hex character. It is the caller's responsibility to move
800 // the input string back to its original position on failure.
801 // ---------------------------------------------------------------------------
803 bool wxURI::ParseH16(const char*& uri
)
809 if(IsHex(*++uri
) && IsHex(*++uri
) && IsHex(*++uri
))
815 // ---------------------------------------------------------------------------
818 // Parses a certain version of an IP address and moves the input string past
819 // it. Returns true if the input string contains the proper version of an ip
820 // address. It is the caller's responsability to move the input string back
821 // to its original position on failure.
822 // ---------------------------------------------------------------------------
824 bool wxURI::ParseIPv4address(const char*& uri
)
826 //IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
828 //dec-octet = DIGIT ; 0-9
829 // / %x31-39 DIGIT ; 10-99
830 // / "1" 2DIGIT ; 100-199
831 // / "2" %x30-34 DIGIT ; 200-249
832 // / "25" %x30-35 ; 250-255
839 //each ip part must be between 0-255 (dupe of version in for loop)
840 if( IsDigit(*++uri
) && IsDigit(*++uri
) &&
841 //100 or less (note !)
842 !( (*(uri
-2) < '2') ||
845 (*(uri
-1) < '5' || (*(uri
-1) == '5' && *uri
<= '5'))
853 if(IsDigit(*uri
))++uri
;
855 //compilers should unroll this loop
856 for(; iIPv4
< 4; ++iIPv4
)
858 if (*uri
!= '.' || !IsDigit(*++uri
))
861 //each ip part must be between 0-255
862 if( IsDigit(*++uri
) && IsDigit(*++uri
) &&
863 //100 or less (note !)
864 !( (*(uri
-2) < '2') ||
867 (*(uri
-1) < '5' || (*(uri
-1) == '5' && *uri
<= '5'))
874 if(IsDigit(*uri
))++uri
;
880 bool wxURI::ParseIPv6address(const char*& uri
)
882 // IPv6address = 6( h16 ":" ) ls32
883 // / "::" 5( h16 ":" ) ls32
884 // / [ h16 ] "::" 4( h16 ":" ) ls32
885 // / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
886 // / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
887 // / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
888 // / [ *4( h16 ":" ) h16 ] "::" ls32
889 // / [ *5( h16 ":" ) h16 ] "::" h16
890 // / [ *6( h16 ":" ) h16 ] "::"
892 size_t numPrefix
= 0,
895 bool bEndHex
= false;
897 for( ; numPrefix
< 6; ++numPrefix
)
912 if(!bEndHex
&& !ParseH16(uri
))
931 if (*uri
!= ':' || *(uri
+1) != ':')
936 while (*--uri
!= ':') {}
939 const char * const start
= uri
;
941 // ls32 = ( h16 ":" h16 ) / IPv4address
942 if (ParseH16(uri
) && *uri
== ':' && ParseH16(uri
))
947 if (ParseIPv4address(uri
))
959 maxPostfix
= 4 - numPrefix
;
963 bool bAllowAltEnding
= maxPostfix
== 0;
965 for(; maxPostfix
!= 0; --maxPostfix
)
967 if(!ParseH16(uri
) || *uri
!= ':')
973 const char * const start
= uri
;
975 // ls32 = ( h16 ":" h16 ) / IPv4address
976 if (ParseH16(uri
) && *uri
== ':' && ParseH16(uri
))
981 if (ParseIPv4address(uri
))
986 if (!bAllowAltEnding
)
990 if(numPrefix
<= 5 && ParseH16(uri
))
996 bool wxURI::ParseIPvFuture(const char*& uri
)
998 // IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
999 if (*++uri
!= 'v' || !IsHex(*++uri
))
1002 while (IsHex(*++uri
))
1005 if (*uri
!= '.' || !(IsUnreserved(*++uri
) || IsSubDelim(*uri
) || *uri
== ':'))
1008 while(IsUnreserved(*++uri
) || IsSubDelim(*uri
) || *uri
== ':') {}
1014 // ---------------------------------------------------------------------------
1017 // Returns true if the passed in character meets the criteria of the method
1018 // ---------------------------------------------------------------------------
1020 // unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1021 bool wxURI::IsUnreserved(char c
)
1023 return IsAlpha(c
) ||
1032 bool wxURI::IsReserved(char c
)
1034 return IsGenDelim(c
) || IsSubDelim(c
);
1037 // gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
1038 bool wxURI::IsGenDelim(char c
)
1049 // sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1050 // / "*" / "+" / "," / ";" / "="
1051 bool wxURI::IsSubDelim(char c
)
1067 bool wxURI::IsHex(char c
)
1069 return IsDigit(c
) ||
1070 (c
>= 'a' && c
<= 'f') ||
1071 (c
>= 'A' && c
<= 'F');
1074 bool wxURI::IsAlpha(char c
)
1076 return (c
>= 'a' && c
<= 'z') || (c
>= 'A' && c
<= 'Z');
1079 bool wxURI::IsDigit(char c
)
1081 return c
>= '0' && c
<= '9';
1084 bool wxURI::IsEndPath(char c
)
1086 return c
== '\0' || c
== '#' || c
== '?';