| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626 |
- /*
- * This source file is part of libRocket, the HTML/CSS Interface Middleware
- *
- * For the latest information, see http://www.librocket.com
- *
- * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- */
- #include "precompiled.h"
- #include "../../Include/Rocket/Core/URL.h"
- #include <stdio.h>
- namespace Rocket {
- namespace Core {
- const char* DEFAULT_PROTOCOL = "file";
- // Constructs an Empty URL.
- URL::URL()
- {
- port = 0;
- url_dirty = false;
- }
- // Constructs a new URL from the given string.
- URL::URL(const String& _url)
- {
- port = 0;
- ROCKET_VERIFY(SetURL(_url));
- }
- // Constructs a new URL from the given string.
- URL::URL(const char* _url)
- {
- port = 0;
- ROCKET_VERIFY(SetURL(_url));
- }
- // Destroys the URL.
- URL::~URL()
- {
- }
- // Assigns a new URL to the object.
- bool URL::SetURL(const String& _url)
- {
- url_dirty = false;
- url = _url;
- // Make sure an Empty URL is completely Empty.
- if (url.Empty())
- {
- protocol.Clear();
- login.Clear();
- password.Clear();
- host.Clear();
- port = 0;
- path.Clear();
- file_name.Clear();
- extension.Clear();
- return true;
- }
- // Find the protocol. This consists of the string appearing before the
- // '://' token (ie, file://, http://).
- const char* host_begin = strchr(_url.CString(), ':');
- if (NULL != host_begin)
- {
- protocol.Assign(_url.CString(), host_begin);
- if (0 != strncmp(host_begin, "://", 3))
- {
- char malformed_terminator[4] = {0, 0, 0, 0};
- strncpy(malformed_terminator, host_begin, 3);
- Log::Message(Log::LT_ERROR, "Malformed protocol identifier found in URL %s; expected %s://, found %s%s.\n", _url.CString(), protocol.CString(), protocol.CString(), malformed_terminator);
- return false;
- }
- host_begin += 3;
- }
- else
- {
- protocol = DEFAULT_PROTOCOL;
- host_begin = _url.CString();
- }
- // We only want to look for a host if a protocol was specified.
- const char* path_begin;
- if (host_begin != _url.CString())
- {
- // Find the host. This is the string appearing after the protocol or after
- // the username:password combination, and terminated either with a colon,
- // if a port is specified, or a forward slash if there is no port.
- // Check for a login pair
- const char* at_symbol = strchr( host_begin, '@' );
- if ( at_symbol )
- {
- String login_password;
- login_password.Assign( host_begin, at_symbol );
- host_begin = at_symbol + 1;
- const char* password_ptr = strchr( login_password.CString(), ':' );
- if ( password_ptr )
- {
- login.Assign( login_password.CString(), password_ptr );
- password.Assign( password_ptr + 1 );
- }
- else
- {
- login = login_password;
- }
- }
- // Get the host portion
- path_begin = strchr(host_begin, '/');
- // Search for the colon in the host name, which will indicate a port.
- const char* port_begin = strchr(host_begin, ':');
- if (NULL != port_begin && (NULL == path_begin || port_begin < path_begin))
- {
- if (1 != sscanf(port_begin, ":%d", &port))
- {
- Log::Message(Log::LT_ERROR, "Malformed port number found in URL %s.\n", _url.CString());
- return false;
- }
- host.Assign(host_begin, port_begin);
- // Don't continue if there is no path.
- if (NULL == path_begin)
- {
- return true;
- }
- // Increment the path string past the trailing slash.
- ++path_begin;
- }
- else
- {
- port = -1;
- if (NULL == path_begin)
- {
- host = host_begin;
- return true;
- }
- else
- {
- // Assign the host name, then increment the path string past the
- // trailing slash.
- host.Assign(host_begin, path_begin);
- ++path_begin;
- }
- }
- }
- else
- {
- path_begin = _url.CString();
- }
-
- // Check for parameters
- String path_segment;
- const char* parameters = strchr(path_begin, '?');
- if ( parameters )
- {
- // Pull the path segment out, so further processing doesn't read the parameters
- path_segment.Assign(path_begin, parameters);
- path_begin = path_segment.CString();
-
- // Loop through all parameters, loading them
- StringList parameter_list;
- StringUtilities::ExpandString( parameter_list, parameters + 1, '&' );
- for ( size_t i = 0; i < parameter_list.size(); i++ )
- {
- // Split into key and value
- StringList key_value;
- StringUtilities::ExpandString( key_value, parameter_list[i], '=' );
- key_value[0] = UrlDecode(key_value[0]);
- if ( key_value.size() == 2 )
- this->parameters[key_value[0]] = UrlDecode(key_value[1]);
- else
- this->parameters[key_value[0]] = "";
- }
- }
- // Find the path. This is the string appearing after the host, terminated
- // by the last forward slash.
- const char* file_name_begin = strrchr(path_begin, '/');
- if (NULL == file_name_begin)
- {
- // No path!
- file_name_begin = path_begin;
- path = "";
- }
- else
- {
- // Copy the path including the trailing slash.
- path.Assign(path_begin, ++file_name_begin);
- // Normalise the path, stripping any ../'s from it
- size_t parent_dir_pos = String::npos;
- while ((parent_dir_pos = path.Find("/..")) != String::npos)
- {
- // If we found a /.. we should be able to find the start of the parent
- // directory, if we can't something wierd has happend, bail
- size_t parent_dir_start_pos = path.RFind("/", parent_dir_pos);
- if (parent_dir_start_pos == String::npos)
- break;
- // Strip out the parent dir and the /..
- path.Erase(parent_dir_start_pos, parent_dir_pos - parent_dir_start_pos + 3);
- // We've altered the URL, mark it dirty
- url_dirty = true;
- }
- }
- // Find the file name. This is the string after the trailing slash of the
- // path, and just before the extension.
- const char* extension_begin = strrchr(file_name_begin, '.');
- if (NULL == extension_begin)
- {
- file_name = file_name_begin;
- extension = "";
- }
- else
- {
- file_name.Assign(file_name_begin, extension_begin);
- extension = extension_begin + 1;
- }
-
- return true;
- }
- // Returns the entire URL.
- const String& URL::GetURL() const
- {
- if (url_dirty)
- ConstructURL();
-
- return url;
- }
- // Sets the URL's protocol.
- bool URL::SetProtocol(const String& _protocol)
- {
- protocol = _protocol;
- url_dirty = true;
- return true;
- }
- // Returns the protocol this URL is utilising.
- const String& URL::GetProtocol() const
- {
- return protocol;
- }
- /// Sets the URL's login
- bool URL::SetLogin( const String& _login )
- {
- login = _login;
- url_dirty = true;
- return true;
- }
- /// Returns the URL's login
- const String& URL::GetLogin() const
- {
- return login;
- }
- /// Sets the URL's password
- bool URL::SetPassword(const String& _password)
- {
- password = _password;
- url_dirty = true;
- return true;
- }
- /// Returns the URL's password
- const String& URL::GetPassword() const
- {
- return password;
- }
- // Sets the URL's host.
- bool URL::SetHost(const String& _host)
- {
- host = _host;
- url_dirty = true;
- return true;
- }
- // Returns the URL's host.
- const String& URL::GetHost() const
- {
- return host;
- }
- // Sets the URL's port number.
- bool URL::SetPort(int _port)
- {
- port = _port;
- url_dirty = true;
- return true;
- }
- // Returns the URL's port number.
- int URL::GetPort() const
- {
- return port;
- }
- // Sets the URL's path.
- bool URL::SetPath(const String& _path)
- {
- path = _path;
- url_dirty = true;
- return true;
- }
- // Prefixes the URL's existing path with the given prefix.
- bool URL::PrefixPath(const String& prefix)
- {
- // If there's no trailing slash on the end of the prefix, add one.
- if (!prefix.Empty() &&
- prefix[prefix.Length() - 1] != '/')
- path = prefix + "/" + path;
- else
- path = prefix + path;
- url_dirty = true;
- return true;
- }
- // Returns the URL's path.
- const String& URL::GetPath() const
- {
- return path;
- }
- // Sets the URL's file name.
- bool URL::SetFileName(const String& _file_name)
- {
- file_name = _file_name;
- url_dirty = true;
- return true;
- }
- // Returns the URL's file name.
- const String& URL::GetFileName() const
- {
- return file_name;
- }
- // Sets the URL's file extension.
- bool URL::SetExtension(const String& _extension)
- {
- extension = _extension;
- url_dirty = true;
- return true;
- }
- // Returns the URL's file extension.
- const String& URL::GetExtension() const
- {
- return extension;
- }
- // Gets the current parameters
- const URL::Parameters& URL::GetParameters() const
- {
- return parameters;
- }
- // Set an individual parameter
- void URL::SetParameter(const String& key, const String& value)
- {
- parameters[key] = value;
- url_dirty = true;
- }
- // Set all parameters
- void URL::SetParameters(const Parameters& _parameters)
- {
- parameters = _parameters;
- url_dirty = true;
- }
- // Clear the parameters
- void URL::ClearParameters()
- {
- parameters.clear();
- }
- // Returns the URL's path, file name and extension.
- String URL::GetPathedFileName() const
- {
- String pathed_file_name = path;
-
- // Append the file name.
- pathed_file_name += file_name;
-
- // Append the extension.
- if (!extension.Empty())
- {
- pathed_file_name.Append(".");
- pathed_file_name += extension;
- }
-
- return pathed_file_name;
- }
- String URL::GetQueryString() const
- {
- String query_string;
-
- int count = 0;
- for ( Parameters::const_iterator itr = parameters.begin(); itr != parameters.end(); ++itr )
- {
- query_string += ( count == 0 ) ? "" : "&";
-
- query_string += UrlEncode((*itr).first);
- query_string += "=";
- query_string += UrlEncode((*itr).second);
-
- count++;
- }
-
- return query_string;
- }
- // Less-than operator for use as a key in STL containers.
- bool URL::operator<(const URL& rhs) const
- {
- if (url_dirty)
- ConstructURL();
- if (rhs.url_dirty)
- rhs.ConstructURL();
-
- return url < rhs.url;
- }
- void URL::ConstructURL() const
- {
- url = "";
- // Append the protocol.
- if (!protocol.Empty() && !host.Empty())
- {
- url = protocol;
- url.Append("://");
- }
- // Append login and password
- if (!login.Empty())
- {
- url.Append( login );
- if (!password.Empty())
- {
- url.Append( ":" );
- url.Append( password );
- }
- url.Append( "@" );
- }
- ROCKET_ASSERTMSG( password.Empty() || ( !password.Empty() && !login.Empty() ), "Can't have a password without a login!" );
- // Append the host.
- url += host;
-
- // Only check ports if there is some host/protocol part
- if ( !url.Empty() )
- {
- if (port > 0)
- {
- ROCKET_ASSERTMSG( !host.Empty(), "Can't have a port without a host!" );
- char port_string[16];
- sprintf(port_string, ":%d/", port);
- url.Append(port_string);
- }
- else
- {
- url.Append("/");
- }
- }
- // Append the path.
- if (!path.Empty())
- {
- url += path;
- }
- // Append the file name.
- url += file_name;
- // Append the extension.
- if (!extension.Empty())
- {
- url.Append(".");
- url += extension;
- }
-
- // Append parameters
- if (!parameters.empty())
- {
- url += "?";
- url += GetQueryString();
- }
-
- url_dirty = false;
- }
- String URL::UrlEncode(const String &value)
- {
- String encoded;
- char hex[4] = {0,0,0,0};
- encoded.Clear();
- const char *value_c = value.CString();
- for (String::size_type i = 0; value_c[i]; i++)
- {
- char c = value_c[i];
- if (IsUnreservedChar(c))
- encoded.Append(c);
- else
- {
- sprintf(hex, "%%%02X", c);
- encoded.Append(hex);
- }
- }
- return encoded;
- }
- String URL::UrlDecode(const String &value)
- {
- String decoded;
- decoded.Clear();
- const char *value_c = value.CString();
- String::size_type value_len = value.Length();
- for (String::size_type i = 0; i < value_len; i++)
- {
- char c = value_c[i];
- if (c == '+')
- {
- decoded.Append(' ' );
- }
- else if (c == '%')
- {
- char *endp;
- String t = value.Substring(i+1, 2);
- int ch = strtol(t.CString(), &endp, 16);
- if (*endp == '\0')
- decoded.Append(char(ch));
- else
- decoded.Append(t);
- i += 2;
- }
- else
- {
- decoded.Append(c);
- }
- }
- return decoded;
- }
- bool URL::IsUnreservedChar(const char in)
- {
- switch (in)
- {
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- case 'a': case 'b': case 'c': case 'd': case 'e':
- case 'f': case 'g': case 'h': case 'i': case 'j':
- case 'k': case 'l': case 'm': case 'n': case 'o':
- case 'p': case 'q': case 'r': case 's': case 't':
- case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
- case 'A': case 'B': case 'C': case 'D': case 'E':
- case 'F': case 'G': case 'H': case 'I': case 'J':
- case 'K': case 'L': case 'M': case 'N': case 'O':
- case 'P': case 'Q': case 'R': case 'S': case 'T':
- case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
- case '-': case '.': case '_': case '~':
- return true;
- default:
- break;
- }
- return false;
- }
- }
- }
|