diff options
Diffstat (limited to 'lib/libUPnP/Neptune/Source/Core/NptUri.cpp')
-rw-r--r-- | lib/libUPnP/Neptune/Source/Core/NptUri.cpp | 912 |
1 files changed, 912 insertions, 0 deletions
diff --git a/lib/libUPnP/Neptune/Source/Core/NptUri.cpp b/lib/libUPnP/Neptune/Source/Core/NptUri.cpp new file mode 100644 index 0000000..e4a7c3b --- /dev/null +++ b/lib/libUPnP/Neptune/Source/Core/NptUri.cpp @@ -0,0 +1,912 @@ +/***************************************************************** +| +| Neptune - URI +| +| Copyright (c) 2002-2008, Axiomatic Systems, LLC. +| All rights reserved. +| +| Redistribution and use in source and binary forms, with or without +| modification, are permitted provided that the following conditions are met: +| * Redistributions of source code must retain the above copyright +| notice, this list of conditions and the following disclaimer. +| * Redistributions in binary form must reproduce the above copyright +| notice, this list of conditions and the following disclaimer in the +| documentation and/or other materials provided with the distribution. +| * Neither the name of Axiomatic Systems nor the +| names of its contributors may be used to endorse or promote products +| derived from this software without specific prior written permission. +| +| THIS SOFTWARE IS PROVIDED BY AXIOMATIC SYSTEMS ''AS IS'' AND ANY +| EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +| DISCLAIMED. IN NO EVENT SHALL AXIOMATIC SYSTEMS BE LIABLE FOR ANY +| DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +| ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +| + ***************************************************************/ + +/*---------------------------------------------------------------------- +| includes ++---------------------------------------------------------------------*/ +#include "NptUri.h" +#include "NptUtils.h" +#include "NptResults.h" + +/*---------------------------------------------------------------------- +| NPT_Uri::ParseScheme ++---------------------------------------------------------------------*/ +NPT_Uri::SchemeId +NPT_Uri::ParseScheme(const NPT_String& scheme) +{ + if (scheme == "http") { + return SCHEME_ID_HTTP; + } else if (scheme == "https") { + return SCHEME_ID_HTTPS; + } else { + return SCHEME_ID_UNKNOWN; + } +} + +/*---------------------------------------------------------------------- +| NPT_Uri::SetScheme ++---------------------------------------------------------------------*/ +void +NPT_Uri::SetScheme(const char* scheme) +{ + m_Scheme = scheme; + m_Scheme.MakeLowercase(); + m_SchemeId = ParseScheme(m_Scheme); +} + +/*---------------------------------------------------------------------- +| NPT_Uri::SetSchemeFromUri ++---------------------------------------------------------------------*/ +NPT_Result +NPT_Uri::SetSchemeFromUri(const char* uri) +{ + const char* start = uri; + char c; + while ((c =*uri++)) { + if (c == ':') { + m_Scheme.Assign(start, (NPT_Size)(uri-start-1)); + m_Scheme.MakeLowercase(); + m_SchemeId = ParseScheme(m_Scheme); + return NPT_SUCCESS; + } else if ((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + (c == '+') || + (c == '.') || + (c == '-')) { + continue; + } else { + break; + } + } + return NPT_ERROR_INVALID_SYNTAX; +} + +/*---------------------------------------------------------------------- +Appendix A. Collected ABNF for URI + + URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] + + hier-part = "//" authority path-abempty + / path-absolute + / path-rootless + / path-empty + + URI-reference = URI / relative-ref + + absolute-URI = scheme ":" hier-part [ "?" query ] + + relative-ref = relative-part [ "?" query ] [ "#" fragment ] + + relative-part = "//" authority path-abempty + / path-absolute + / path-noscheme + / path-empty + + scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + + authority = [ userinfo "@" ] host [ ":" port ] + userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) + host = IP-literal / IPv4address / reg-name + port = *DIGIT + + IP-literal = "[" ( IPv6address / IPvFuture ) "]" + + IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) + + IPv6address = 6( h16 ":" ) ls32 + / "::" 5( h16 ":" ) ls32 + / [ h16 ] "::" 4( h16 ":" ) ls32 + / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 + / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 + / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 + / [ *4( h16 ":" ) h16 ] "::" ls32 + / [ *5( h16 ":" ) h16 ] "::" h16 + / [ *6( h16 ":" ) h16 ] "::" + + h16 = 1*4HEXDIG + ls32 = ( h16 ":" h16 ) / IPv4address + IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet + dec-octet = DIGIT ; 0-9 + / %x31-39 DIGIT ; 10-99 + / "1" 2DIGIT ; 100-199 + / "2" %x30-34 DIGIT ; 200-249 + / "25" %x30-35 ; 250-255 + + reg-name = *( unreserved / pct-encoded / sub-delims ) + + path = path-abempty ; begins with "/" or is empty + / path-absolute ; begins with "/" but not "//" + / path-noscheme ; begins with a non-colon segment + / path-rootless ; begins with a segment + / path-empty ; zero characters + + path-abempty = *( "/" segment ) + path-absolute = "/" [ segment-nz *( "/" segment ) ] + path-noscheme = segment-nz-nc *( "/" segment ) + path-rootless = segment-nz *( "/" segment ) + path-empty = 0<pchar> + + segment = *pchar + segment-nz = 1*pchar + segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) + ; non-zero-length segment without any colon ":" + + pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + + query = *( pchar / "/" / "?" ) + + fragment = *( pchar / "/" / "?" ) + + pct-encoded = "%" HEXDIG HEXDIG + + unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + reserved = gen-delims / sub-delims + gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" + sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + / "*" / "+" / "," / ";" / "=" + +---------------------------------------------------------------------*/ + +#define NPT_URI_ALWAYS_ENCODE " !\"<>\\^`{|}" + +/*---------------------------------------------------------------------- +| NPT_Uri::PathCharsToEncode ++---------------------------------------------------------------------*/ +const char* const +NPT_Uri::PathCharsToEncode = NPT_URI_ALWAYS_ENCODE "?#[]"; + +/*---------------------------------------------------------------------- +| NPT_Uri::QueryCharsToEncode ++---------------------------------------------------------------------*/ +const char* const +NPT_Uri::QueryCharsToEncode = NPT_URI_ALWAYS_ENCODE "#[]"; + +/*---------------------------------------------------------------------- +| NPT_Uri::FragmentCharsToEncode ++---------------------------------------------------------------------*/ +const char* const +NPT_Uri::FragmentCharsToEncode = NPT_URI_ALWAYS_ENCODE "[]"; + +/*---------------------------------------------------------------------- +| NPT_Uri::UnsafeCharsToEncode ++---------------------------------------------------------------------*/ +const char* const +NPT_Uri::UnsafeCharsToEncode = NPT_URI_ALWAYS_ENCODE; + +/*---------------------------------------------------------------------- +| NPT_Uri::PercentEncode ++---------------------------------------------------------------------*/ +NPT_String +NPT_Uri::PercentEncode(const char* str, const char* chars, bool encode_percents) +{ + NPT_String encoded; + + // check args + if (str == NULL) return encoded; + + // reserve at least the size of the current uri + encoded.Reserve(NPT_StringLength(str)); + + // process each character + char escaped[3]; + escaped[0] = '%'; + while (unsigned char c = *str++) { + bool encode = false; + if (encode_percents && c == '%') { + encode = true; + } else if (c < ' ' || c > '~') { + encode = true; + } else { + const char* match = chars; + while (*match) { + if (c == *match) { + encode = true; + break; + } + ++match; + } + } + if (encode) { + // encode + NPT_ByteToHex(c, &escaped[1], true); + encoded.Append(escaped, 3); + } else { + // no encoding required + encoded += c; + } + } + + return encoded; +} + +/*---------------------------------------------------------------------- +| NPT_Uri::PercentDecode ++---------------------------------------------------------------------*/ +NPT_String +NPT_Uri::PercentDecode(const char* str) +{ + NPT_String decoded; + + // check args + if (str == NULL) return decoded; + + // reserve at least the size of the current uri + decoded.Reserve(NPT_StringLength(str)); + + // process each character + while (unsigned char c = *str++) { + if (c == '%') { + // needs to be unescaped + unsigned char unescaped; + if (NPT_SUCCEEDED(NPT_HexToByte(str, unescaped))) { + decoded += unescaped; + str += 2; + } else { + // not a valid escape sequence, just keep the % + decoded += c; + } + } else { + // no unescaping required + decoded += c; + } + } + + return decoded; +} + +/*---------------------------------------------------------------------- +| NPT_UrlQuery::NPT_UrlQuery ++---------------------------------------------------------------------*/ +NPT_UrlQuery::NPT_UrlQuery(const char* query) +{ + Parse(query); +} + +/*---------------------------------------------------------------------- +| NPT_UrlQuery::UrlEncode ++---------------------------------------------------------------------*/ +NPT_String +NPT_UrlQuery::UrlEncode(const char* str, bool encode_percents) +{ + NPT_String encoded = NPT_Uri::PercentEncode( + str, + ";/?:@&=+$," /* reserved as defined in RFC 2396 */ + "\"#<>\\^`{|}", /* other unsafe chars */ + encode_percents); + encoded.Replace(' ','+'); + + return encoded; +} + +/*---------------------------------------------------------------------- +| NPT_UrlQuery::UrlDecode ++---------------------------------------------------------------------*/ +NPT_String +NPT_UrlQuery::UrlDecode(const char* str) +{ + NPT_String decoded(str); + decoded.Replace('+', ' '); + return NPT_Uri::PercentDecode(decoded); +} + +/*---------------------------------------------------------------------- +| NPT_UrlQuery::Field::Field ++---------------------------------------------------------------------*/ +NPT_UrlQuery::Field::Field(const char* name, const char* value, bool encoded) +{ + if (encoded) { + m_Name = name; + m_Value = value; + } else { + m_Name = UrlEncode(name); + m_Value = UrlEncode(value); + } +} + +/*---------------------------------------------------------------------- +| NPT_UrlQuery::ToString ++---------------------------------------------------------------------*/ +NPT_String +NPT_UrlQuery::ToString() +{ + NPT_String encoded; + bool separator = false; + for (NPT_List<Field>::Iterator it = m_Fields.GetFirstItem(); + it; + ++it) { + Field& field = *it; + if (separator) encoded += "&"; + separator = true; + encoded += field.m_Name; + encoded += "="; + encoded += field.m_Value; + } + + return encoded; +} + +/*---------------------------------------------------------------------- +| NPT_UrlQuery::Parse ++---------------------------------------------------------------------*/ +NPT_Result +NPT_UrlQuery::Parse(const char* query) +{ + const char* cursor = query; + NPT_String name; + NPT_String value; + bool in_name = true; + do { + if (*cursor == '\0' || *cursor == '&') { + AddField(name, value, true); + name.SetLength(0); + value.SetLength(0); + in_name = true; + } else if (*cursor == '=' && in_name) { + in_name = false; + } else { + if (in_name) { + name += *cursor; + } else { + value += *cursor; + } + } + } while (*cursor++); + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_UrlQuery::AddField ++---------------------------------------------------------------------*/ +NPT_Result +NPT_UrlQuery::AddField(const char* name, const char* value, bool encoded) +{ + return m_Fields.Add(Field(name, value, encoded)); +} + +/*---------------------------------------------------------------------- +| NPT_UrlQuery::SetField ++---------------------------------------------------------------------*/ +NPT_Result +NPT_UrlQuery::SetField(const char* name, const char* value, bool encoded) +{ + NPT_String ename; + if (encoded) { + ename = name; + } else { + ename = UrlEncode(name); + } + for (NPT_List<Field>::Iterator it = m_Fields.GetFirstItem(); + it; + ++it) { + Field& field = *it; + if (field.m_Name == ename) { + if (encoded) { + field.m_Value = value; + } else { + field.m_Value = UrlEncode(value); + } + return NPT_SUCCESS; + } + } + + // field not found, add it + return AddField(name, value, encoded); +} + +/*---------------------------------------------------------------------- +| NPT_UrlQuery::GetField ++---------------------------------------------------------------------*/ +const char* +NPT_UrlQuery::GetField(const char* name) +{ + NPT_String ename = UrlEncode(name); + for (NPT_List<Field>::Iterator it = m_Fields.GetFirstItem(); + it; + ++it) { + Field& field = *it; + if (field.m_Name == ename) return field.m_Value; + } + + // field not found + return NULL; +} + +/*---------------------------------------------------------------------- +| types ++---------------------------------------------------------------------*/ +typedef enum { + NPT_URL_PARSER_STATE_START, + NPT_URL_PARSER_STATE_SCHEME, + NPT_URL_PARSER_STATE_LEADING_SLASH, + NPT_URL_PARSER_STATE_HOST, + NPT_URL_PARSER_STATE_HOST_IPV6_ADDR, + NPT_URL_PARSER_STATE_PORT, + NPT_URL_PARSER_STATE_PATH, + NPT_URL_PARSER_STATE_QUERY +} NPT_UrlParserState; + +/*---------------------------------------------------------------------- +| NPT_Url::NPT_Url ++---------------------------------------------------------------------*/ +NPT_Url::NPT_Url() : + m_HostIsIpv6Address(false), + m_Port(NPT_URL_INVALID_PORT), + m_Path("/"), + m_HasQuery(false), + m_HasFragment(false) +{ +} + +/*---------------------------------------------------------------------- +| NPT_Url::NPT_Url ++---------------------------------------------------------------------*/ +NPT_Url::NPT_Url(const char* url, NPT_UInt16 default_port) : + m_HostIsIpv6Address(false), + m_Port(NPT_URL_INVALID_PORT), + m_HasQuery(false), + m_HasFragment(false) +{ + // try to parse + if (NPT_FAILED(Parse(url, default_port))) { + Reset(); + } +} + +/*---------------------------------------------------------------------- +| NPT_Url::Parse ++---------------------------------------------------------------------*/ +NPT_Result +NPT_Url::Parse(const char* url, NPT_UInt16 default_port) +{ + // check parameters + if (url == NULL) return NPT_ERROR_INVALID_PARAMETERS; + + // set the uri scheme + NPT_Result result = SetSchemeFromUri(url); + if (NPT_FAILED(result)) return result; + + // set the default port + if (default_port) { + m_Port = default_port; + } else { + switch (m_SchemeId) { + case SCHEME_ID_HTTP: m_Port = NPT_URL_DEFAULT_HTTP_PORT; break; + case SCHEME_ID_HTTPS: m_Port = NPT_URL_DEFAULT_HTTPS_PORT; break; + default: break; + } + } + + // move to the scheme-specific part + url += m_Scheme.GetLength()+1; + + // intialize the parser + NPT_UrlParserState state = NPT_URL_PARSER_STATE_START; + const char* mark = url; + + // parse the URL + char c; + do { + c = *url++; + switch (state) { + case NPT_URL_PARSER_STATE_START: + if (c == '/') { + state = NPT_URL_PARSER_STATE_LEADING_SLASH; + } else { + return NPT_ERROR_INVALID_SYNTAX; + } + break; + + case NPT_URL_PARSER_STATE_LEADING_SLASH: + if (c == '/') { + state = NPT_URL_PARSER_STATE_HOST; + mark = url; + } else { + return NPT_ERROR_INVALID_SYNTAX; + } + break; + + case NPT_URL_PARSER_STATE_HOST_IPV6_ADDR: + if (c == ']') { + state = NPT_URL_PARSER_STATE_HOST; + } + break; + + case NPT_URL_PARSER_STATE_HOST: + if (c == '[' && url == mark+1) { + // start of an IPv6 address + state = NPT_URL_PARSER_STATE_HOST_IPV6_ADDR; + } else if (c == ':' || c == '/' || c == '\0' || c == '?' || c == '#') { + NPT_Size host_length = (NPT_Size)(url-1-mark); + if (host_length > 2 && mark[0] == '[' && mark[host_length-1] == ']') { + m_Host.Assign(mark+1, host_length-2); + m_HostIsIpv6Address = true; + } else { + m_Host.Assign(mark, host_length); + m_HostIsIpv6Address = false; + } + if (c == ':') { + mark = url; + m_Port = 0; + state = NPT_URL_PARSER_STATE_PORT; + } else { + mark = url-1; + state = NPT_URL_PARSER_STATE_PATH; + } + } + break; + + case NPT_URL_PARSER_STATE_PORT: + if (c >= '0' && c <= '9') { + unsigned int val = m_Port*10+(c-'0'); + if (val > 65535) { + m_Port = NPT_URL_INVALID_PORT; + return NPT_ERROR_INVALID_SYNTAX; + } + m_Port = val; + } else if (c == '/' || c == '\0') { + mark = url-1; + state = NPT_URL_PARSER_STATE_PATH; + } else { + // invalid character + m_Port = NPT_URL_INVALID_PORT; + return NPT_ERROR_INVALID_SYNTAX; + } + break; + + case NPT_URL_PARSER_STATE_PATH: + if (*mark) { + return ParsePathPlus(mark); + } + break; + + default: + break; + } + } while (c); + + // if we get here, the path is implicit + m_Path = "/"; + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_Url::NPT_Url ++---------------------------------------------------------------------*/ +NPT_Url::NPT_Url(const char* scheme, + const char* host, + NPT_UInt16 port, + const char* path, + const char* query, + const char* fragment) : + m_Host(host), + m_HostIsIpv6Address(false), + m_Port(port), + m_Path(path), + m_HasQuery(query != NULL), + m_Query(query), + m_HasFragment(fragment != NULL), + m_Fragment(fragment) +{ + SetScheme(scheme); + + // deal with IPv6 addresses + if (m_Host.StartsWith("[") && m_Host.EndsWith("]")) { + m_HostIsIpv6Address = true; + m_Host = m_Host.SubString(1, m_Host.GetLength()-2); + } +} + +/*---------------------------------------------------------------------- +| NPT_Url::Reset ++---------------------------------------------------------------------*/ +void +NPT_Url::Reset() +{ + m_Host.SetLength(0); + m_HostIsIpv6Address = false; + m_Port = 0; + m_Path.SetLength(0); + m_HasQuery = false; + m_Query.SetLength(0); + m_HasFragment = false; + m_Fragment.SetLength(0); +} + +/*---------------------------------------------------------------------- +| NPT_Url::IsValid ++---------------------------------------------------------------------*/ +bool +NPT_Url::IsValid() const +{ + switch (m_SchemeId) { + case SCHEME_ID_HTTP: + case SCHEME_ID_HTTPS: + return m_Port != NPT_URL_INVALID_PORT && !m_Host.IsEmpty(); + break; + + default: + return !m_Scheme.IsEmpty(); + } +} + +/*---------------------------------------------------------------------- +| NPT_Url::SetHost ++---------------------------------------------------------------------*/ +NPT_Result +NPT_Url::SetHost(const char* host) +{ + const char* port; + if (*host == '[') { + const char* host_end = host+1; + while (*host_end && *host_end != ']') ++host_end; + if (*host_end != ']') { + return NPT_ERROR_INVALID_SYNTAX; + } + port = host_end+1; + if (*port && *port != ':') { + return NPT_ERROR_INVALID_SYNTAX; + } + m_Host.Assign(host+1, (NPT_Size)(host_end-host-1)); + m_HostIsIpv6Address = true; + } else { + port = host; + while (*port && *port != ':') port++; + m_Host.Assign(host, (NPT_Size)(port-host)); + m_HostIsIpv6Address = false; + } + + if (*port) { + unsigned int port_number; + // parse the port number but ignore errors (be lenient) + if (NPT_SUCCEEDED(NPT_ParseInteger(port+1, port_number, false))) { + if (port_number > 65535) { + return NPT_ERROR_OUT_OF_RANGE; + } + m_Port = (NPT_UInt16)port_number; + } else { + return NPT_ERROR_INVALID_SYNTAX; + } + } + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_Url::SetPort ++---------------------------------------------------------------------*/ +NPT_Result +NPT_Url::SetPort(NPT_UInt16 port) +{ + m_Port = port; + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_Url::SetPath ++---------------------------------------------------------------------*/ +NPT_Result +NPT_Url::SetPath(const char* path, bool encoded) +{ + if (encoded) { + m_Path = path; + } else { + m_Path = PercentEncode(path, PathCharsToEncode); + } + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_Url::ParsePathPlus ++---------------------------------------------------------------------*/ +NPT_Result +NPT_Url::ParsePathPlus(const char* path_plus) +{ + // check parameters + if (path_plus == NULL) return NPT_ERROR_INVALID_PARAMETERS; + + // reset any existing values + m_Path.SetLength(0); + m_Query.SetLength(0); + m_Fragment.SetLength(0); + m_HasQuery = false; + m_HasFragment = false; + +#ifdef _WIN32 + // Skip the leading '/' if there is an absolute path starting with + // a drive letter on Windows. + if (path_plus[0] == '/' && + ((path_plus[1] >= 'a' && path_plus[1] <= 'z') || + (path_plus[1] >= 'A' && path_plus[1] <= 'Z')) && + path_plus[2] == ':') + { + ++path_plus; + } +#endif + + // intialize the parser + NPT_UrlParserState state = NPT_URL_PARSER_STATE_PATH; + const char* mark = path_plus; + + // parse the path+ + char c; + do { + c = *path_plus++; + switch (state) { + case NPT_URL_PARSER_STATE_PATH: + if (c == '\0' || c == '?' || c == '#') { + if (path_plus-1 > mark) { + m_Path.Append(mark, (NPT_Size)(path_plus-1-mark)); + } + if (c == '?') { + m_HasQuery = true; + state = NPT_URL_PARSER_STATE_QUERY; + mark = path_plus; + } else if (c == '#') { + m_HasFragment = true; + m_Fragment = path_plus; + return NPT_SUCCESS; + } + } + break; + + case NPT_URL_PARSER_STATE_QUERY: + if (c == '\0' || c == '#') { + m_Query.Assign(mark, (NPT_Size)(path_plus-1-mark)); + if (c == '#') { + m_HasFragment = true; + m_Fragment = path_plus; + } + return NPT_SUCCESS; + } + break; + + default: + break; + } + } while (c); + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_Url::SetQuery ++---------------------------------------------------------------------*/ +NPT_Result +NPT_Url::SetQuery(const char* query, bool encoded) +{ + if (encoded) { + m_Query = query; + } else { + m_Query = PercentEncode(query, QueryCharsToEncode); + } + m_HasQuery = query!=NULL && NPT_StringLength(query)>0; + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_Url::SetFragment ++---------------------------------------------------------------------*/ +NPT_Result +NPT_Url::SetFragment(const char* fragment, bool encoded) +{ + if (encoded) { + m_Fragment = fragment; + } else { + m_Fragment = PercentEncode(fragment, FragmentCharsToEncode); + } + m_HasFragment = fragment!=NULL; + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| NPT_Url::ToRequestString ++---------------------------------------------------------------------*/ +NPT_String +NPT_Url::ToRequestString(bool with_fragment) const +{ + NPT_String result; + NPT_Size length = m_Path.GetLength()+1; + if (m_HasQuery) length += 1+m_Query.GetLength(); + if (with_fragment) length += 1+m_Fragment.GetLength(); + result.Reserve(length); + + if (m_Path.IsEmpty()) { + result += "/"; + } else { +#if defined(_WIN32) + // prepend a '/' if the path starts with the drive letter on Windows + if (((m_Path[0] >= 'a' && m_Path[0] <= 'z') || + (m_Path[0] >= 'A' && m_Path[0] <= 'Z')) && + m_Path[1] == ':') + { + result += "/"; + } +#endif + result += m_Path; + } + if (m_HasQuery) { + result += "?"; + result += m_Query; + } + if (with_fragment && m_HasFragment) { + result += "#"; + result += m_Fragment; + } + return result; +} + +/*---------------------------------------------------------------------- +| NPT_Url::ToStringWithDefaultPort ++---------------------------------------------------------------------*/ +NPT_String +NPT_Url::ToStringWithDefaultPort(NPT_UInt16 default_port, bool with_fragment) const +{ + NPT_String result; + NPT_String request = ToRequestString(with_fragment); + NPT_Size length = m_Scheme.GetLength()+3+m_Host.GetLength()+6+request.GetLength(); + + if (m_HostIsIpv6Address) { + length += 2; + } + + result.Reserve(length); + result += m_Scheme; + result += "://"; + if (m_HostIsIpv6Address) { + result += "["; + } + result += m_Host; + if (m_HostIsIpv6Address) { + result += "]"; + } + if (m_Port != default_port) { + NPT_String port = NPT_String::FromInteger(m_Port); + result += ":"; + result += port; + } + result += request; + return result; +} + +/*---------------------------------------------------------------------- +| NPT_Url::ToString ++---------------------------------------------------------------------*/ +NPT_String +NPT_Url::ToString(bool with_fragment) const +{ + return ToStringWithDefaultPort(0, with_fragment); +} |