summaryrefslogtreecommitdiffstats
path: root/lib/libUPnP/Neptune/Source/Core/NptUri.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libUPnP/Neptune/Source/Core/NptUri.cpp')
-rw-r--r--lib/libUPnP/Neptune/Source/Core/NptUri.cpp912
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);
+}