// // VMime library (http://www.vmime.org) // Copyright (C) 2002 Vincent Richard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 3 of // the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // Linking this library statically or dynamically with other modules is making // a combined work based on this library. Thus, the terms and conditions of // the GNU General Public License cover the whole combination. // #include "vmime/utility/url.hpp" #include "vmime/parserHelpers.hpp" #include "vmime/utility/urlUtils.hpp" #include "vmime/exception.hpp" #include namespace vmime { namespace utility { // Unspecified port const port_t url::UNSPECIFIED_PORT = static_cast (-1); // Known protocols const string url::PROTOCOL_FILE = "file"; const string url::PROTOCOL_HTTP = "http"; const string url::PROTOCOL_FTP = "ftp"; url::url(const string& s) { parse(s); } url::url(const url& u) { operator=(u); } url::url( const string& protocol, const string& host, const port_t port, const string& path, const string& username, const string& password ) : m_protocol(protocol), m_username(username), m_password(password), m_host(host), m_port(port), m_path(path) { } url& url::operator=(const url& u) { m_protocol = u.m_protocol; m_username = u.m_username; m_password = u.m_password; m_host = u.m_host; m_port = u.m_port; m_path = u.m_path; m_params = u.m_params; return *this; } url& url::operator=(const string& s) { parse(s); return *this; } url::operator string() const { return build(); } const string url::build() const { std::ostringstream oss; oss.imbue(std::locale::classic()); oss << m_protocol << "://"; if (!m_username.empty()) { oss << urlUtils::encode(m_username); if (!m_password.empty()) { oss << ":"; oss << urlUtils::encode(m_password); } oss << "@"; } oss << urlUtils::encode(m_host); if (m_port != UNSPECIFIED_PORT) { oss << ":"; oss << m_port; } if (!m_path.empty()) { oss << "/"; oss << urlUtils::encode(m_path); } if (!m_params.empty()) { if (m_path.empty()) { oss << "/"; } oss << "?"; for (std::map ::const_iterator it = m_params.begin() ; it != m_params.end() ; ++it) { if (it != m_params.begin()) { oss << "&"; } oss << urlUtils::encode((*it).first); oss << "="; oss << urlUtils::encode((*it).second); } } return oss.str(); } void url::parse(const string& str) { // Protocol const size_t protoEnd = str.find("://"); if (protoEnd == string::npos) { throw exceptions::malformed_url("No protocol separator"); } const string proto = utility::stringUtils::toLower(string(str.begin(), str.begin() + protoEnd)); // Username/password size_t slashPos = str.find('/', protoEnd + 3); if (slashPos == string::npos) { slashPos = str.length(); } size_t atPos = str.rfind('@', slashPos); string hostPart; string username; string password; if (proto == PROTOCOL_FILE) { // No user name, password and host part. slashPos = protoEnd + 3; } else { if (atPos != string::npos && atPos < slashPos) { const string userPart(str.begin() + protoEnd + 3, str.begin() + atPos); const size_t colonPos = userPart.find(':'); if (colonPos == string::npos) { username = userPart; } else { username = string(userPart.begin(), userPart.begin() + colonPos); password = string(userPart.begin() + colonPos + 1, userPart.end()); } hostPart = string(str.begin() + atPos + 1, str.begin() + slashPos); } else { hostPart = string(str.begin() + protoEnd + 3, str.begin() + slashPos); } } // Host/port const size_t colonPos = hostPart.find(':'); string host; string port; if (colonPos == string::npos) { host = utility::stringUtils::trim(hostPart); } else { host = utility::stringUtils::trim(string(hostPart.begin(), hostPart.begin() + colonPos)); port = utility::stringUtils::trim(string(hostPart.begin() + colonPos + 1, hostPart.end())); } // Path string path = utility::stringUtils::trim(string(str.begin() + slashPos, str.end())); string params; size_t paramSep = path.find_first_of('?'); if (paramSep != string::npos) { params = string(path.begin() + paramSep + 1, path.end()); path.erase(path.begin() + paramSep, path.end()); } if (path == "/") { path.clear(); } // Some sanity check if (proto.empty()) { throw exceptions::malformed_url("No protocol specified"); } else if (host.empty()) { // Accept empty host (eg. "file:///home/vincent/mydoc") if (proto != PROTOCOL_FILE) { throw exceptions::malformed_url("No host specified"); } } bool onlyDigit = true; for (string::const_iterator it = port.begin() ; onlyDigit && it != port.end() ; ++it) { onlyDigit = parserHelpers::isDigit(*it); } if (!onlyDigit) { throw exceptions::malformed_url("Port can only contain digits"); } std::istringstream iss(port); iss.imbue(std::locale::classic()); port_t portNum = 0; iss >> portNum; if (portNum == 0) { portNum = UNSPECIFIED_PORT; } // Extract parameters m_params.clear(); if (!params.empty()) { size_t pos = 0; do { const size_t start = pos; pos = params.find_first_of('&', pos); const size_t equal = params.find_first_of('=', start); const size_t end = (pos == string::npos ? params.length() : pos); string name; string value; if (equal == string::npos || equal > pos) { // no value name = string(params.begin() + start, params.begin() + end); value = name; } else { name = string(params.begin() + start, params.begin() + equal); value = string(params.begin() + equal + 1, params.begin() + end); } name = urlUtils::decode(name); value = urlUtils::decode(value); m_params[name] = value; if (pos != string::npos) { ++pos; } } while (pos != string::npos); } // Now, save URL parts m_protocol = proto; m_username = urlUtils::decode(username); m_password = urlUtils::decode(password); m_host = urlUtils::decode(host); m_port = portNum; m_path = urlUtils::decode(path); } const string& url::getProtocol() const { return m_protocol; } void url::setProtocol(const string& protocol) { m_protocol = protocol; } const string& url::getUsername() const { return m_username; } void url::setUsername(const string& username) { m_username = username; } const string& url::getPassword() const { return m_password; } void url::setPassword(const string& password) { m_password = password; } const string& url::getHost() const { return m_host; } void url::setHost(const string& host) { m_host = host; } port_t url::getPort() const { return m_port; } void url::setPort(const port_t port) { m_port = port; } const string& url::getPath() const { return m_path; } void url::setPath(const string& path) { m_path = path; } const std::map & url::getParams() const { return m_params; } std::map & url::getParams() { return m_params; } } // utility } // vmime