diff --git a/src/utility/url.cpp b/src/utility/url.cpp index 919a3610..cf991b86 100644 --- a/src/utility/url.cpp +++ b/src/utility/url.cpp @@ -69,6 +69,8 @@ url& url::operator=(const url& u) m_path = u.m_path; + m_params = u.m_params; + return (*this); } @@ -120,6 +122,29 @@ const string url::build() const oss << urlUtils::encode(m_path); } + const std::vector params + = m_params.getPropertyList(); + + if (!params.empty()) + { + if (m_path.empty()) + oss << "/"; + + oss << "?"; + + for (unsigned int i = 0 ; i < params.size() ; ++i) + { + const propertySet::property* prop = params[i]; + + if (i != 0) + oss << "&"; + + oss << urlUtils::encode(prop->getName()); + oss << "="; + oss << urlUtils::encode(prop->getValue()); + } + } + return (oss.str()); } @@ -191,6 +216,15 @@ void url::parse(const string& str) // Path string path = utility::stringUtils::trim(string(str.begin() + slashPos, str.end())); + string params; + + string::size_type 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(); @@ -224,6 +258,48 @@ void url::parse(const string& str) if (portNum == 0) portNum = UNSPECIFIED_PORT; + // Extract parameters + m_params.removeAllProperties(); + + if (!params.empty()) + { + string::size_type pos = 0; + + do + { + const string::size_type start = pos; + + pos = params.find_first_of('&', pos); + + const string::size_type equal = params.find_first_of('=', start); + const string::size_type 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.setProperty(name, value); + + if (pos != string::npos) + ++pos; + } + while (pos != string::npos); + } + // Now, save URL parts m_protocol = proto; @@ -309,5 +385,17 @@ void url::setPath(const string& path) } +const propertySet& url::getParams() const +{ + return (m_params); +} + + +propertySet& url::getParams() +{ + return (m_params); +} + + } // utility } // vmime diff --git a/src/utility/urlUtils.cpp b/src/utility/urlUtils.cpp index 70a988cf..c22fc2a7 100644 --- a/src/utility/urlUtils.cpp +++ b/src/utility/urlUtils.cpp @@ -34,7 +34,8 @@ const string urlUtils::encode(const string& s) { const char_t c = *it; - if (parserHelpers::isPrint(c) && !parserHelpers::isSpace(c) && c != '%') + if (parserHelpers::isPrint(c) && !parserHelpers::isSpace(c) && + c != '%' && c != '=' && c != '?' && c != '&') { result += c; } diff --git a/tests/utility/urlTest.cpp b/tests/utility/urlTest.cpp index cb139987..963febda 100644 --- a/tests/utility/urlTest.cpp +++ b/tests/utility/urlTest.cpp @@ -149,13 +149,57 @@ namespace assert_eq("1.7", "/pa\xabth/", u1.getPath()); } + void testParse4() + { + // Test parameters + vmime::utility::url u1("", ""); + + assert_eq("1.1", true, parseHelper(u1, "proto://host/path?p1=v1&p2=v2")); + assert_eq("1.2", "v1", u1.getParams().getProperty ("p1")); + assert_eq("1.3", "v2", u1.getParams().getProperty ("p2")); + assert_eq("1.4", "/path", u1.getPath()); + + vmime::utility::url u2("", ""); + + assert_eq("2.1", true, parseHelper(u2, "proto://host/path?p1=v1&p2")); + assert_eq("2.2", "v1", u2.getParams().getProperty ("p1")); + assert_eq("2.3", "p2", u2.getParams().getProperty ("p2")); + assert_eq("2.4", "/path", u2.getPath()); + + vmime::utility::url u3("", ""); + + assert_eq("3.1", true, parseHelper(u3, "proto://host/?p1=v1&p2=v2")); + assert_eq("3.2", "v1", u3.getParams().getProperty ("p1")); + assert_eq("3.3", "v2", u3.getParams().getProperty ("p2")); + assert_eq("3.4", "", u3.getPath()); + + vmime::utility::url u4("", ""); + + assert_eq("4.1", true, parseHelper(u4, "proto://host/path?p1=%3D&%3D=v2")); + assert_eq("4.2", "=", u4.getParams().getProperty ("p1")); + assert_eq("4.3", "v2", u4.getParams().getProperty ("=")); + assert_eq("4.4", "/path", u4.getPath()); + } + void testGenerate() { vmime::utility::url u1("proto", "host", 12345, "path", "user", "password"); assert_eq("1", "proto://user:password@host:12345/path", static_cast (u1)); - // TODO: more tests + vmime::utility::url u2("proto", "host"); + assert_eq("2", "proto://host", static_cast (u2)); + + vmime::utility::url u3("proto", "host"); + u3.getParams().setProperty("p1", "v1"); + assert_eq("3.1", "proto://host/?p1=v1", + static_cast (u3)); + u3.getParams().setProperty("p2", "v2"); + assert_eq("3.2", "proto://host/?p1=v1&p2=v2", + static_cast (u3)); + u3.getParams().setProperty("&", "="); + assert_eq("3.3", "proto://host/?p1=v1&p2=v2&%26=%3D", + static_cast (u3)); } void testUtilsEncode() @@ -196,6 +240,7 @@ namespace add("Parse1", testcase(this, "Parse1", &urlTest::testParse1)); add("Parse2", testcase(this, "Parse2", &urlTest::testParse2)); add("Parse3", testcase(this, "Parse3", &urlTest::testParse3)); + add("Parse4", testcase(this, "Parse4", &urlTest::testParse4)); add("Generate", testcase(this, "Generate", &urlTest::testGenerate)); add("UtilsEncode", testcase(this, "UtilsEncode", &urlTest::testUtilsEncode)); add("UtilsDecode", testcase(this, "UtilsDecode", &urlTest::testUtilsDecode)); diff --git a/vmime/utility/url.hpp b/vmime/utility/url.hpp index 89f63c7b..1bb207a0 100644 --- a/vmime/utility/url.hpp +++ b/vmime/utility/url.hpp @@ -23,6 +23,7 @@ #include "vmime/types.hpp" #include "vmime/base.hpp" +#include "vmime/propertySet.hpp" namespace vmime { @@ -153,6 +154,17 @@ public: */ void setPath(const string& path); + /** Return the parameters of the URL (read-only). + * + * @return parameters + */ + const propertySet& getParams() const; + + /** Return the parameters of the URL. + * + * @return parameters + */ + propertySet& getParams(); /** Build a string URL from this object. */ @@ -179,6 +191,8 @@ private: port_t m_port; string m_path; + + propertySet m_params; };