From 6571ffc2c8aadd52f2bb6b0f1931e1920761b5b8 Mon Sep 17 00:00:00 2001 From: Jean Flach Date: Tue, 1 Sep 2015 11:11:45 +0200 Subject: [PATCH] Update url parser fixes #10039 --- lib/remote/url-characters.hpp | 2 +- lib/remote/url.cpp | 135 ++++++++++++++++++++++++++++++++-- lib/remote/url.hpp | 23 +++++- test/CMakeLists.txt | 1 + test/remote-url.cpp | 26 +++++++ 5 files changed, 177 insertions(+), 10 deletions(-) diff --git a/lib/remote/url-characters.hpp b/lib/remote/url-characters.hpp index ab2c97939..73a098be7 100644 --- a/lib/remote/url-characters.hpp +++ b/lib/remote/url-characters.hpp @@ -31,7 +31,7 @@ #define ACSCHEME ALPHA NUMERIC ".-+" //authority = [ userinfo "@" ] host [ ":" port ] -#define ACUSERINFO UNRESERVED SUB_DELIMS ":" +#define ACUSERINFO UNRESERVED SUB_DELIMS #define ACHOST UNRESERVED SUB_DELIMS #define ACPORT NUMERIC diff --git a/lib/remote/url.cpp b/lib/remote/url.cpp index e908d05e7..6f1e2f2e8 100644 --- a/lib/remote/url.cpp +++ b/lib/remote/url.cpp @@ -27,6 +27,8 @@ using namespace icinga; +Url::Url() {} + Url::Url(const String& base_url) { String url = base_url; @@ -93,7 +95,43 @@ String Url::GetScheme(void) const String Url::GetAuthority(void) const { - return m_Authority; + if (m_Host.IsEmpty()) + return ""; + + String auth; + if (!m_Username.IsEmpty()) { + auth = m_Username; + if (!m_Password.IsEmpty()) + auth += ":" + m_Password; + auth += "@"; + } + + auth += m_Host; + + if (!m_Port.IsEmpty()) + auth += ":" + m_Port; + + return auth; +} + +String Url::GetUsername(void) const +{ + return m_Username; +} + +String Url::GetPassword(void) const +{ + return m_Password; +} + +String Url::GetHost(void) const +{ + return m_Host; +} + +String Url::GetPort(void) const +{ + return m_Port; } const std::vector& Url::GetPath(void) const @@ -132,16 +170,59 @@ String Url::GetFragment(void) const { return m_Fragment; } +void Url::SetScheme(const String& scheme) +{ + m_Scheme = scheme; +} -String Url::Format(void) const +void Url::SetAuthority(const String& username, const String& password, const String& host, const String& port) +{ + m_Username = username; + m_Password = password; + m_Host = host; + m_Port = port; +} + +void Url::SetPath(const std::vector& path) +{ + m_Path = path; +} + +void Url::SetQuery(const std::map >& query) +{ + m_Query = query; +} + +void Url::AddQueryElement(const String& name, const String& value) +{ + std::map >::iterator it = m_Query.find(name); + if (it == m_Query.end()) { + m_Query[name] = std::vector(); + m_Query[name].push_back(value); + } else + m_Query[name].push_back(value); +} + +void Url::SetQueryElements(const String& name, const std::vector& values) +{ + m_Query[name] = values; +} + +void Url::SetFragment(const String& fragment) { + m_Fragment = fragment; +} + +String Url::Format(bool print_credentials) const { String url; if (!m_Scheme.IsEmpty()) url += m_Scheme + ":"; - if (!m_Authority.IsEmpty()) - url += "//" + m_Authority; + if (print_credentials && !GetAuthority().IsEmpty()) + url += "//" + GetAuthority(); + else if (!GetHost().IsEmpty()) + url += "//" + GetHost() + (!GetPort().IsEmpty() ? ":" + GetPort() : ""); if (m_Path.empty()) url += "/"; @@ -194,9 +275,49 @@ bool Url::ParseScheme(const String& scheme) bool Url::ParseAuthority(const String& authority) { - m_Authority = authority.SubStr(2); - //Just safe the Authority and don't care about the details - return (ValidateToken(m_Authority, ACHOST GEN_DELIMS)); + String auth = authority.SubStr(2); + size_t pos = auth.Find("@"); + if (pos != String::NPos && pos != 0) { + if (!Url::ParseUserinfo(auth.SubStr(0, pos))) + return false; + auth = auth.SubStr(pos+1); + } + + pos = auth.Find(":"); + if (pos != String::NPos) { + if (pos == 0 || pos == auth.GetLength() - 1 || !Url::ParsePort(auth.SubStr(pos+1))) + return false; + } + + m_Host = auth.SubStr(0, pos-1); + return ValidateToken(m_Host, ACHOST); +} + +bool Url::ParseUserinfo(const String& userinfo) +{ + size_t pos = userinfo.Find(":"); + m_Username = userinfo.SubStr(0, pos-1); + if (!ValidateToken(m_Username, ACUSERINFO)) + return false; + m_Username = Utility::UnescapeString(m_Username); + if (pos != String::NPos && pos != userinfo.GetLength() - 1) { + //Password + m_Password = userinfo.SubStr(pos+1); + if (!ValidateToken(m_Username, ACUSERINFO)) + return false; + m_Password = Utility::UnescapeString(m_Password); + } else + m_Password = ""; + + return true; +} + +bool Url::ParsePort(const String& port) +{ + m_Port = Utility::UnescapeString(m_Port); + if (!ValidateToken(m_Port, ACPORT)) + return false; + return true; } bool Url::ParsePath(const String& path) diff --git a/lib/remote/url.hpp b/lib/remote/url.hpp index f51444568..d2ad7d458 100644 --- a/lib/remote/url.hpp +++ b/lib/remote/url.hpp @@ -41,27 +41,46 @@ class I2_REMOTE_API Url : public Object public: DECLARE_PTR_TYPEDEFS(Url); + Url(); Url(const String& url); - String Format(void) const; + String Format(bool print_credentials = false) const; String GetScheme(void) const; String GetAuthority(void) const; + String GetUsername(void) const; + String GetPassword(void) const; + String GetHost(void) const; + String GetPort(void) const; const std::vector& GetPath(void) const; const std::map >& GetQuery(void) const; String GetQueryElement(const String& name) const; const std::vector& GetQueryElements(const String& name) const; String GetFragment(void) const; + void SetScheme(const String& scheme); + void SetAuthority(const String& username, const String& password, + const String& host, const String& port); + void SetPath(const std::vector& path); + void SetQuery(const std::map >& query); + void AddQueryElement(const String& name, const String& query); + void SetQueryElements(const String& name, const std::vector& query); + void SetFragment(const String& fragment); + private: String m_Scheme; - String m_Authority; + String m_Username; + String m_Password; + String m_Host; + String m_Port; std::vector m_Path; std::map > m_Query; String m_Fragment; bool ParseScheme(const String& scheme); bool ParseAuthority(const String& authority); + bool ParseUserinfo(const String& userinfo); + bool ParsePort(const String& port); bool ParsePath(const String& path); bool ParseQuery(const String& query); bool ParseFragment(const String& fragment); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 53fa3ef88..b8cbca6b2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -111,6 +111,7 @@ add_boost_test(base remote_apiuser/check_password remote_url/id_and_path remote_url/parameters + remote_url/get_and_set remote_url/format remote_url/illegal_legal_strings ) diff --git a/test/remote-url.cpp b/test/remote-url.cpp index 1dcc8696c..21cd99c66 100644 --- a/test/remote-url.cpp +++ b/test/remote-url.cpp @@ -21,6 +21,7 @@ #include "remote/url.hpp" #include #include +#include using namespace icinga; @@ -42,6 +43,31 @@ BOOST_AUTO_TEST_CASE(id_and_path) BOOST_CHECK(url->GetPath() == PathCorrect); } +BOOST_AUTO_TEST_CASE(get_and_set) +{ + Url::Ptr url = new Url(); + url->SetScheme("ftp"); + url->SetAuthority("Horst", "Seehofer", "koenigreich.bayern", "1918"); + std::vector p = boost::assign::list_of("path")("to")("münchen"); + url->SetPath(p); + BOOST_CHECK(url->Format(true) == "ftp://Horst:Seehofer@koenigreich.bayern:1918/path/to/m%C3%BCnchen"); + + std::map > m; + std::vector v1 = boost::assign::list_of("hip")("hip")("hurra"); + std::vector v2 = boost::assign::list_of("äü^ä+#ül-"); + std::vector v3 = boost::assign::list_of("1")("2"); + m.insert(std::pair >("shout", v1)); + m.insert(std::pair >("sonderzeichen", v2)); + url->SetQuery(m); + url->SetQueryElements("count", v3); + url->AddQueryElement("count", "3"); + + std::map > mn = url->GetQuery(); + BOOST_CHECK(mn["shout"][0] == v1[0]); + BOOST_CHECK(mn["sonderzeichen"][0] == v2[0]); + BOOST_CHECK(mn["count"][2] == "3"); +} + BOOST_AUTO_TEST_CASE(parameters) { Url::Ptr url = new Url("https://icinga.org/hya/?rain=karl&rair=robert&foo[]=bar");