icinga2/lib/remote/url.cpp

410 lines
9.6 KiB
C++
Raw Normal View History

2015-06-26 15:37:47 +02:00
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
* *
* 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 2 *
* 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 St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "base/array.hpp"
#include "base/utility.hpp"
#include "base/objectlock.hpp"
#include "remote/url.hpp"
#include "remote/url-characters.hpp"
2015-06-26 15:37:47 +02:00
#include <boost/tokenizer.hpp>
#include <boost/foreach.hpp>
using namespace icinga;
2015-09-01 11:11:45 +02:00
Url::Url() {}
2015-06-26 15:37:47 +02:00
Url::Url(const String& base_url)
{
String url = base_url;
if (url.GetLength() == 0)
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid URL Empty URL."));
size_t pHelper = url.Find(":");
2015-07-29 13:09:42 +02:00
if (pHelper != String::NPos) {
2015-06-26 15:37:47 +02:00
if (!ParseScheme(url.SubStr(0, pHelper)))
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid URL Scheme."));
url = url.SubStr(pHelper + 1);
}
if (*url.Begin() != '/')
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid URL: '/' expected after scheme."));
if (url.GetLength() == 1) {
return;
}
2015-07-29 13:09:42 +02:00
if (*(url.Begin() + 1) == '/') {
2015-06-26 15:37:47 +02:00
pHelper = url.Find("/", 2);
if (pHelper == String::NPos)
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid URL: Missing '/' after authority."));
if (!ParseAuthority(url.SubStr(0, pHelper)))
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid URL Authority"));
url = url.SubStr(pHelper);
}
if (*url.Begin() == '/') {
pHelper = url.FindFirstOf("#?");
if (!ParsePath(url.SubStr(1, pHelper - 1)))
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid URL Path"));
if (pHelper != String::NPos)
url = url.SubStr(pHelper);
} else
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid URL: Missing path."));
if (*url.Begin() == '?') {
pHelper = url.Find("#");
if (!ParseQuery(url.SubStr(1, pHelper - 1)))
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid URL Query"));
if (pHelper != String::NPos)
url = url.SubStr(pHelper);
}
if (*url.Begin() == '#') {
if (!ParseFragment(url.SubStr(1)))
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid URL Fragment"));
}
}
String Url::GetScheme(void) const
{
return m_Scheme;
}
String Url::GetAuthority(void) const
{
2015-09-01 11:11:45 +02:00
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;
2015-06-26 15:37:47 +02:00
}
const std::vector<String>& Url::GetPath(void) const
{
return m_Path;
}
2015-07-29 13:09:42 +02:00
const std::map<String, std::vector<String> >& Url::GetQuery(void) const
2015-06-26 15:37:47 +02:00
{
return m_Query;
}
2015-07-29 13:09:42 +02:00
String Url::GetQueryElement(const String& name) const
2015-06-26 15:37:47 +02:00
{
2015-07-29 13:09:42 +02:00
std::map<String, std::vector<String> >::const_iterator it = m_Query.find(name);
2015-06-26 15:37:47 +02:00
if (it == m_Query.end())
2015-07-29 13:09:42 +02:00
return String();
2015-06-26 15:37:47 +02:00
2015-07-29 13:09:42 +02:00
return it->second.back();
2015-06-26 15:37:47 +02:00
}
2015-07-29 13:09:42 +02:00
const std::vector<String>& Url::GetQueryElements(const String& name) const
{
std::map<String, std::vector<String> >::const_iterator it = m_Query.find(name);
if (it == m_Query.end()) {
static std::vector<String> emptyVector;
return emptyVector;
}
return it->second;
}
2015-06-26 15:37:47 +02:00
String Url::GetFragment(void) const
{
return m_Fragment;
}
2015-09-01 11:11:45 +02:00
void Url::SetScheme(const String& scheme)
{
m_Scheme = scheme;
}
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<String>& path)
{
m_Path = path;
}
void Url::SetQuery(const std::map<String, std::vector<String> >& query)
{
m_Query = query;
}
2015-06-26 15:37:47 +02:00
2015-09-01 11:11:45 +02:00
void Url::AddQueryElement(const String& name, const String& value)
{
std::map<String, std::vector<String> >::iterator it = m_Query.find(name);
if (it == m_Query.end()) {
m_Query[name] = std::vector<String>();
m_Query[name].push_back(value);
} else
m_Query[name].push_back(value);
}
void Url::SetQueryElements(const String& name, const std::vector<String>& values)
{
m_Query[name] = values;
}
void Url::SetFragment(const String& fragment) {
m_Fragment = fragment;
}
String Url::Format(bool print_credentials) const
2015-06-26 15:37:47 +02:00
{
2015-07-29 13:09:42 +02:00
String url;
2015-06-26 15:37:47 +02:00
if (!m_Scheme.IsEmpty())
url += m_Scheme + ":";
2015-09-01 11:11:45 +02:00
if (print_credentials && !GetAuthority().IsEmpty())
url += "//" + GetAuthority();
else if (!GetHost().IsEmpty())
url += "//" + GetHost() + (!GetPort().IsEmpty() ? ":" + GetPort() : "");
2015-06-26 15:37:47 +02:00
if (m_Path.empty())
url += "/";
else {
BOOST_FOREACH (const String p, m_Path) {
url += "/";
url += Utility::EscapeString(p, ACPATHSEGMENT, false);
}
}
2015-07-29 13:09:42 +02:00
String param;
2015-06-26 15:37:47 +02:00
if (!m_Query.empty()) {
2015-07-29 13:09:42 +02:00
typedef std::pair<String, std::vector<String> > kv_pair;
2015-06-26 15:37:47 +02:00
BOOST_FOREACH (const kv_pair& kv, m_Query) {
String key = Utility::EscapeString(kv.first, ACQUERY, false);
if (param.IsEmpty())
param = "?";
else
param += "&";
2015-07-29 13:09:42 +02:00
String temp;
BOOST_FOREACH (const String s, kv.second) {
if (!temp.IsEmpty())
temp += "&";
2015-06-26 15:37:47 +02:00
2015-07-29 13:09:42 +02:00
temp += key + "[]=" + Utility::EscapeString(s, ACQUERY, false);
2015-06-26 15:37:47 +02:00
}
2015-07-29 13:09:42 +02:00
param += temp;
2015-06-26 15:37:47 +02:00
}
}
url += param;
if (!m_Fragment.IsEmpty())
url += "#" + Utility::EscapeString(m_Fragment, ACFRAGMENT, false);
return url;
}
bool Url::ParseScheme(const String& scheme)
{
m_Scheme = scheme;
if (scheme.FindFirstOf(ALPHA) != 0)
return false;
return (ValidateToken(scheme, ACSCHEME));
}
bool Url::ParseAuthority(const String& authority)
{
2015-09-01 11:11:45 +02:00
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;
2015-06-26 15:37:47 +02:00
}
bool Url::ParsePath(const String& path)
{
std::string pathStr = path;
boost::char_separator<char> sep("/");
boost::tokenizer<boost::char_separator<char> > tokens(pathStr, sep);
BOOST_FOREACH(const String& token, tokens) {
if (token.IsEmpty())
continue;
if (!ValidateToken(token, ACPATHSEGMENT))
2015-06-26 15:37:47 +02:00
return false;
String decodedToken = Utility::UnescapeString(token);
2015-06-26 15:37:47 +02:00
m_Path.push_back(decodedToken);
}
return true;
}
bool Url::ParseQuery(const String& query)
{
//Tokenizer does not like String AT ALL
std::string queryStr = query;
boost::char_separator<char> sep("&");
boost::tokenizer<boost::char_separator<char> > tokens(queryStr, sep);
BOOST_FOREACH(const String& token, tokens) {
size_t pHelper = token.Find("=");
2015-07-29 13:09:42 +02:00
if (pHelper == 0)
// /?foo=bar&=bar == invalid
return false;
2015-06-26 15:37:47 +02:00
String key = token.SubStr(0, pHelper);
String value = Empty;
2015-07-29 13:09:42 +02:00
if (pHelper != token.GetLength()-1)
value = token.SubStr(pHelper+1);
2015-06-26 15:37:47 +02:00
2015-07-29 13:09:42 +02:00
if (!ValidateToken(value, ACQUERY))
2015-06-26 15:37:47 +02:00
return false;
2015-07-29 13:09:42 +02:00
value = Utility::UnescapeString(value);
2015-06-26 15:37:47 +02:00
pHelper = key.Find("[]");
2015-07-29 13:09:42 +02:00
if (pHelper == 0 || (pHelper != String::NPos && pHelper != key.GetLength()-2))
return false;
2015-06-26 15:37:47 +02:00
2015-07-29 13:09:42 +02:00
key = key.SubStr(0, pHelper);
if (!ValidateToken(key, ACQUERY))
return false;
2015-06-26 15:37:47 +02:00
2015-07-29 13:09:42 +02:00
key = Utility::UnescapeString(key);
2015-06-26 15:37:47 +02:00
2015-07-29 13:09:42 +02:00
std::map<String, std::vector<String> >::iterator it = m_Query.find(key);
2015-06-26 15:37:47 +02:00
2015-07-29 13:09:42 +02:00
if (it == m_Query.end()) {
m_Query[key] = std::vector<String>();
m_Query[key].push_back(value);
} else
m_Query[key].push_back(value);
2015-06-26 15:37:47 +02:00
}
return true;
}
bool Url::ParseFragment(const String& fragment)
{
m_Fragment = Utility::UnescapeString(fragment);
return ValidateToken(fragment, ACFRAGMENT);
}
bool Url::ValidateToken(const String& token, const String& symbols)
{
BOOST_FOREACH (const char c, token.CStr()) {
if (symbols.FindFirstOf(c) == String::NPos)
return false;
}
return true;
}