/**
* \file zypp/url/UrlBase.cc
*/
-#include <zypp/url/UrlBase.h>
-#include <zypp/base/String.h>
+#include "zypp/url/UrlBase.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/Regex.h"
#include <stdexcept>
-// FIXME:
-#if defined(WITH_URL_PARSE_DEBUG)
+#include <climits>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
#include <iostream>
-#endif
+
+// in the Estonian locale, a-z excludes t, for example. #302525
+// http://en.wikipedia.org/wiki/Estonian_alphabet
+#define a_zA_Z "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+// ---------------------------------------------------------------
+/*
+** authority = //[user [:password] @ ] host [:port]
+**
+** host = hostname | IPv4 | "[" IPv6-IP "]" | "[v...]"
+*/
+#define RX_VALID_SCHEME "^[" a_zA_Z "][" a_zA_Z "0-9\\.+-]*$"
+
+#define RX_VALID_PORT "^[0-9]{1,5}$"
+
+#define RX_VALID_HOSTNAME "^[[:alnum:]]+([\\.-][[:alnum:]]+)*$"
+
+#define RX_VALID_HOSTIPV4 \
+ "^([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$"
+
+#define RX_VALID_HOSTIPV6 \
+ "^\\[[:a-fA-F0-9]+(:[0-9]{1,3}(\\.[0-9]{1,3}){3})?\\]$"
+
//////////////////////////////////////////////////////////////////////
namespace zypp
// ---------------------------------------------------------------
+ /*
+ ** URL asString() view option constants:
+ */
+ const ViewOption ViewOption::WITH_SCHEME = 0x0001;
+ const ViewOption ViewOption::WITH_USERNAME = 0x0002;
+ const ViewOption ViewOption::WITH_PASSWORD = 0x0004;
+ const ViewOption ViewOption::WITH_HOST = 0x0008;
+ const ViewOption ViewOption::WITH_PORT = 0x0010;
+ const ViewOption ViewOption::WITH_PATH_NAME = 0x0020;
+ const ViewOption ViewOption::WITH_PATH_PARAMS = 0x0040;
+ const ViewOption ViewOption::WITH_QUERY_STR = 0x0080;
+ const ViewOption ViewOption::WITH_FRAGMENT = 0x0100;
+ const ViewOption ViewOption::EMPTY_AUTHORITY = 0x0200;
+ const ViewOption ViewOption::EMPTY_PATH_NAME = 0x0400;
+ const ViewOption ViewOption::EMPTY_PATH_PARAMS = 0x0800;
+ const ViewOption ViewOption::EMPTY_QUERY_STR = 0x1000;
+ const ViewOption ViewOption::EMPTY_FRAGMENT = 0x2000;
+ const ViewOption ViewOption::DEFAULTS = 0x07bb;
+ /*
+ const ViewOption ViewOption::DEFAULTS =
+ ViewOption::WITH_SCHEME +
+ ViewOption::WITH_USERNAME +
+ ViewOption::WITH_HOST +
+ ViewOption::WITH_PORT +
+ ViewOption::WITH_PATH_NAME +
+ ViewOption::WITH_QUERY_STR +
+ ViewOption::WITH_FRAGMENT +
+ ViewOption::EMPTY_AUTHORITY +
+ ViewOption::EMPTY_PATH_NAME;
+ */
+
+ // ---------------------------------------------------------------
+ ViewOption::ViewOption()
+ : opt(0x07bb)
+ {}
+
+ // ---------------------------------------------------------------
+ ViewOption::ViewOption(int option)
+ : opt(option)
+ {}
+
+
+ // ---------------------------------------------------------------
+ /*
+ ** Behaviour configuration variables.
+ */
+ typedef std::map< std::string, std::string > UrlConfig;
+
+
+ // ---------------------------------------------------------------
/**
- * authority = //[user [:password] @ ] host [:port]
- *
- * host = hostname | IPv4 | "[" IPv6-IP "]" | "[v...]"
+ * \brief Internal data used by UrlBase.
*/
- #define RX_SPLIT_AUTHORITY \
- "^(([^:@]*)" "([:]([^@]*))?" "@)?" "(\\[[^]]+\\]|[^:]+)?" "([:](.*))?"
+ class UrlBaseData
+ {
+ public:
+ UrlBaseData()
+ {}
+
+ UrlBaseData(const UrlConfig &conf)
+ : config(conf)
+ {}
+
+ UrlConfig config;
+ ViewOptions vopts;
+
+ std::string scheme;
+ std::string user;
+ std::string pass;
+ std::string host;
+ std::string port;
+ std::string pathname;
+ std::string pathparams;
+ std::string querystr;
+ std::string fragment;
+ };
// ---------------------------------------------------------------
+ /*
+ ** Anonymous/internal utility namespace:
+ */
namespace // anonymous
{
// -------------------------------------------------------------
inline void
checkUrlData(const std::string &data,
- const std::string &name,
- const std::string ®x)
+ const std::string &name,
+ const std::string ®x,
+ bool show=true)
{
if( regx.empty() || regx == "^$")
{
- throw std::invalid_argument(
- std::string("Url scheme does not allow a " +
- name)
- );
+ ZYPP_THROW(UrlNotAllowedException(
+ str::form(_("Url scheme does not allow a %s"), name.c_str())
+ ));
}
else
- if( !str::regex_match(data, str::regex(regx)))
{
- throw std::invalid_argument(
- std::string("Invalid " + name + " argument '" +
- data + "'")
- );
+ bool valid = false;
+ try
+ {
+ str::regex rex(regx);
+ valid = str::regex_match(data, rex);
+ }
+ catch( ... )
+ {}
+
+ if( !valid)
+ {
+ if( show)
+ {
+ ZYPP_THROW(UrlBadComponentException(
+ str::form(_("Invalid %s component '%s'"),
+ name.c_str(), data.c_str())
+ ));
+ }
+ else
+ {
+ ZYPP_THROW(UrlBadComponentException(
+ str::form(_("Invalid %s component"), name.c_str())
+ ));
+ }
+ }
}
}
+
} // namespace
// ---------------------------------------------------------------
UrlBase::UrlBase()
- : m_data( new UrlData())
+ : m_data( new UrlBaseData())
{
configure();
}
// ---------------------------------------------------------------
UrlBase::UrlBase(const UrlBase &url)
- : m_data( new UrlData( *(url.m_data)))
+ : m_data( new UrlBaseData( *(url.m_data)))
{
}
const std::string &pathdata,
const std::string &querystr,
const std::string &fragment)
- : m_data( new UrlData())
+ : m_data( new UrlBaseData())
{
configure();
init(scheme, authority, pathdata, querystr, fragment);
const std::string &querystr,
const std::string &fragment)
{
- setScheme(scheme);
+ if ( scheme.empty() && *pathdata.c_str() == '/' )
+ setScheme("file");
+ else
+ setScheme(scheme);
+
setAuthority(authority);
setPathData(pathdata);
setQueryString(querystr);
- setFragment(fragment);
+ setFragment(fragment, zypp::url::E_ENCODED);
}
config("psep_querystr", "&");
config("vsep_querystr", "=");
- config("safe_username", "");
- config("safe_password", "");
+ config("safe_username", "~!$&'()*+=,;");
+ config("safe_password", "~!$&'()*+=,:;");
config("safe_hostname", "[:]");
- config("safe_pathname", "/");
- config("safe_pathparams", "");
- config("safe_querystr", "");
- config("safe_fragment", "");
-
- config("rx_scheme", "^[a-zA-Z][a-zA-Z0-9\\._-]*$");
- config("rx_username", ".*");
- config("rx_password", ".*");
- config("rx_hostname", ".*"); // FIXME
- config("rx_port", ".*"); // FIXME
- config("rx_pathname", ".*");
- config("rx_pathparams", ".*");
- config("rx_querystr", ".*");
- config("rx_fragment", ".*");
-
- config("show_password", "n");
-
- config("empty_authority", "y");
- config("empty_pathname", "y");
- config("empty_pathparms", "n");
- config("empty_querystr", "n");
- config("empty_fragment", "n");
+ config("safe_pathname", "~!$&'()*+=,:@/");
+ config("safe_pathparams", "~!$&'()*+=,:;@/");
+ config("safe_querystr", "~!$&'()*+=,:;@/?");
+ config("safe_fragment", "~!$&'()*+=,:;@/?");
+
+ // y=yes (allowed)
+ // n=no (disallowed, exception if !empty)
+ config("with_authority", "y");
+ config("with_port", "y");
+
+ // y=yes (required but don't throw if empty)
+ // n=no (not required, ignore if empty)
+ // m=mandatory (exception if empty)
+ config("require_host", "n");
+ config("require_pathname","n");
+
+ // y=yes (encode 2. slash even if authority present)
+ // n=no (don't encode 2. slash if authority present)
+ config("path_encode_slash2", "n");
+
+ config("rx_username", "^([" a_zA_Z "0-9!$&'\\(\\)*+=,;~\\._-]|%[a-fA-F0-9]{2})+$");
+ config("rx_password", "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:;~\\._-]|%[a-fA-F0-9]{2})+$");
+
+ config("rx_pathname", "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:@/~\\._-]|%[a-fA-F0-9]{2})+$");
+ config("rx_pathparams", "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:;@/~\\._-]|%[a-fA-F0-9]{2})+$");
+
+ config("rx_querystr", "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:;@/?~\\._-]|%[a-fA-F0-9]{2})+$");
+ config("rx_fragment", "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:;@/?~\\._-]|%[a-fA-F0-9]{2})+$");
}
// ---------------------------------------------------------------
+ ViewOptions
+ UrlBase::getViewOptions() const
+ {
+ return m_data->vopts;
+ }
+
+
+ // ---------------------------------------------------------------
+ void
+ UrlBase::setViewOptions(const ViewOptions &vopts)
+ {
+ m_data->vopts = vopts;
+ }
+
+
+ // ---------------------------------------------------------------
void
UrlBase::clear()
{
- url::UrlConfig config(m_data->config);
- *m_data = UrlData();
+ zypp::url::UrlConfig config(m_data->config);
+ zypp::url::ViewOptions vopts(m_data->vopts);
+ *m_data = UrlBaseData();
m_data->config = config;
+ m_data->vopts = vopts;
}
// ---------------------------------------------------------------
- UrlBase::Schemes
+ zypp::url::UrlSchemes
UrlBase::getKnownSchemes() const
{
- return Schemes();
+ return UrlSchemes();
}
bool
UrlBase::isKnownScheme(const std::string &scheme) const
{
- Schemes schemes(getKnownSchemes());
- Schemes::const_iterator s;
+ std::string lscheme( str::toLower(scheme));
+ UrlSchemes schemes( getKnownSchemes());
+ UrlSchemes::const_iterator s;
+
for(s=schemes.begin(); s!=schemes.end(); ++s)
{
- if( scheme == *s)
+ if( lscheme == str::toLower(*s))
return true;
}
return false;
bool
UrlBase::isValidScheme(const std::string &scheme) const
{
- if(scheme.empty() ||
- str::regex_match(scheme, str::regex(config("rx_scheme"))))
+ bool valid = false;
+ try
+ {
+ str::regex rex(RX_VALID_SCHEME);
+ valid = str::regex_match(scheme, rex);
+ }
+ catch( ... )
+ {}
+
+ if(valid)
{
- Schemes schemes(getKnownSchemes());
+ std::string lscheme( str::toLower(scheme));
+ UrlSchemes schemes( getKnownSchemes());
if( schemes.empty())
return true;
- Schemes::const_iterator s;
+ UrlSchemes::const_iterator s;
for(s=schemes.begin(); s!=schemes.end(); ++s)
{
- if( scheme == *s)
+ if( lscheme == str::toLower(*s))
return true;
}
}
// ---------------------------------------------------------------
+ bool
+ UrlBase::isValid() const
+ {
+ /*
+ ** scheme is the only mandatory component
+ ** for all url's and is already verified,
+ ** (except for empty Url instances), so
+ ** Url with empty scheme is never valid.
+ */
+ if( getScheme().empty())
+ return false;
+
+ std::string host( getHost(zypp::url::E_ENCODED));
+ if( host.empty() && config("require_host") != "n")
+ return false;
+
+ std::string path( getPathName(zypp::url::E_ENCODED));
+ if( path.empty() && config("require_pathname") != "n")
+ return false;
+
+ /*
+ ** path has to begin with "/" if authority avaliable
+ ** if host is set after the pathname, we can't throw
+ */
+ if( !host.empty() && !path.empty() && path.at(0) != '/')
+ return false;
+
+ return true;
+ }
+
+
+ // ---------------------------------------------------------------
std::string
- UrlBase::toString() const
+ UrlBase::asString() const
{
- std::string url;
- UrlData tmp;
+ return asString(getViewOptions());
+ }
- tmp.scheme = getScheme();
- if( !tmp.scheme.empty())
- {
- url += tmp.scheme + ":";
- tmp.host = getHost(zypp::url::E_ENCODED);
- if( !tmp.host.empty())
+ // ---------------------------------------------------------------
+ std::string
+ UrlBase::asString(const zypp::url::ViewOptions &opts) const
+ {
+ std::string url;
+ UrlBaseData tmp;
+
+ if( opts.has(ViewOptions::WITH_SCHEME))
+ {
+ tmp.scheme = getScheme();
+ if( !tmp.scheme.empty())
{
- url += "//";
+ url += tmp.scheme + ":";
- tmp.user = getUsername(zypp::url::E_ENCODED);
- if( !tmp.user.empty())
+ if( opts.has(ViewOptions::WITH_HOST))
{
- url += tmp.user;
-
- tmp.pass = getPassword(zypp::url::E_ENCODED);
- if( !tmp.pass.empty())
+ tmp.host = getHost(zypp::url::E_ENCODED);
+ if( !tmp.host.empty())
{
- if(config("show_password") == "y")
+ url += "//";
+
+ if( opts.has(ViewOptions::WITH_USERNAME))
{
- url += ":" + tmp.pass;
+ tmp.user = getUsername(zypp::url::E_ENCODED);
+ if( !tmp.user.empty())
+ {
+ url += tmp.user;
+
+ if( opts.has(ViewOptions::WITH_PASSWORD))
+ {
+ tmp.pass = getPassword(zypp::url::E_ENCODED);
+ if( !tmp.pass.empty())
+ {
+ url += ":" + tmp.pass;
+ }
+ }
+ url += "@";
+ }
}
- }
- url += "@";
- }
- url += tmp.host;
+ url += tmp.host;
- tmp.port = getPort();
- if( !tmp.port.empty())
+ if( opts.has(ViewOptions::WITH_PORT))
+ {
+ tmp.port = getPort();
+ if( !tmp.port.empty())
+ {
+ url += ":" + tmp.port;
+ }
+ }
+ }
+ else if( opts.has(ViewOptions::EMPTY_AUTHORITY))
+ {
+ url += "//";
+ }
+ }
+ else if( opts.has(ViewOptions::EMPTY_AUTHORITY))
{
- url += ":" + tmp.port;
+ url += "//";
}
}
- else if(config("empty_authority") == "y")
- {
- url += "//";
- }
}
- tmp.pathname = getPathName(zypp::url::E_ENCODED);
- if( !tmp.pathname.empty())
+ if( opts.has(ViewOptions::WITH_PATH_NAME))
{
- if( !tmp.host.empty() && tmp.pathname.at(0) != '/')
+ tmp.pathname = getPathName(zypp::url::E_ENCODED);
+ if( !tmp.pathname.empty())
{
- url += "/";
- }
- url += tmp.pathname;
+ if(url.find("/") != std::string::npos)
+ {
+ // Url contains authority (that may be empty),
+ // we may need a rewrite of the encoded path.
+ tmp.pathname = cleanupPathName(tmp.pathname, true);
+ if(tmp.pathname.at(0) != '/')
+ {
+ url += "/";
+ }
+ }
+ url += tmp.pathname;
- tmp.pathparams = getPathParams();
- if( !tmp.pathparams.empty())
- {
- url += ";" + tmp.pathparams;
+ if( opts.has(ViewOptions::WITH_PATH_PARAMS))
+ {
+ tmp.pathparams = getPathParams();
+ if( !tmp.pathparams.empty())
+ {
+ url += ";" + tmp.pathparams;
+ }
+ else if( opts.has(ViewOptions::EMPTY_PATH_PARAMS))
+ {
+ url += ";";
+ }
+ }
}
- else if(config("empty_pathparams") == "y")
+ else if( opts.has(ViewOptions::EMPTY_PATH_NAME)
+ && url.find("/") != std::string::npos)
{
- url += ";";
+ url += "/";
+ if( opts.has(ViewOptions::EMPTY_PATH_PARAMS))
+ {
+ url += ";";
+ }
}
}
- else if(config("empty_pathname") == "y")
+
+ if( opts.has(ViewOptions::WITH_QUERY_STR))
{
- url += "/";
- if(config("empty_pathparams") == "y")
+ tmp.querystr = getQueryString();
+ if( !tmp.querystr.empty())
{
- url += ";";
+ url += "?" + tmp.querystr;
+ }
+ else if( opts.has(ViewOptions::EMPTY_QUERY_STR))
+ {
+ url += "?";
}
}
- tmp.querystr = getQueryString();
- if( !tmp.querystr.empty())
- {
- url += "?" + tmp.querystr;
- }
- else if(config("empty_querystr") == "y")
- {
- url += "?";
- }
-
- tmp.fragment = getFragment(zypp::url::E_ENCODED);
- if( !tmp.fragment.empty())
- {
- url += "#" + tmp.fragment;
- }
- else if(config("empty_fragment") == "y")
+ if( opts.has(ViewOptions::WITH_FRAGMENT))
{
- url += "#";
+ tmp.fragment = getFragment(zypp::url::E_ENCODED);
+ if( !tmp.fragment.empty())
+ {
+ url += "#" + tmp.fragment;
+ }
+ else if( opts.has(ViewOptions::EMPTY_FRAGMENT))
+ {
+ url += "#";
+ }
}
return url;
if(eflag == zypp::url::E_DECODED)
return zypp::url::decode(m_data->pathname);
else
- return m_data->pathname;
+ return cleanupPathName(m_data->pathname);
}
UrlBase::getPathParamsVec() const
{
zypp::url::ParamVec pvec;
- zypp::url::split(
- pvec,
- getPathParams(),
- config("psep_pathparam")
- );
+ if( config("psep_pathparam").empty())
+ {
+ pvec.push_back(getPathParams());
+ }
+ else
+ {
+ zypp::url::split(
+ pvec,
+ getPathParams(),
+ config("psep_pathparam")
+ );
+ }
return pvec;
}
zypp::url::ParamMap
UrlBase::getPathParamsMap(EEncoding eflag) const
{
+ if( config("psep_pathparam").empty() ||
+ config("vsep_pathparam").empty())
+ {
+ ZYPP_THROW(UrlNotSupportedException(
+ "Path parameter parsing not supported for this URL"
+ ));
+ }
zypp::url::ParamMap pmap;
zypp::url::split(
pmap,
// ---------------------------------------------------------------
+ std::string
+ UrlBase::getPathParam(const std::string ¶m, EEncoding eflag) const
+ {
+ zypp::url::ParamMap pmap( getPathParamsMap( eflag));
+ zypp::url::ParamMap::const_iterator i( pmap.find(param));
+
+ return i != pmap.end() ? i->second : std::string();
+ }
+
+
+ // ---------------------------------------------------------------
zypp::url::ParamVec
UrlBase::getQueryStringVec() const
{
zypp::url::ParamVec pvec;
- zypp::url::split(
- pvec,
- getQueryString(),
- config("psep_querystr")
- );
+ if( config("psep_querystr").empty())
+ {
+ pvec.push_back(getQueryString());
+ }
+ else
+ {
+ zypp::url::split(
+ pvec,
+ getQueryString(),
+ config("psep_querystr")
+ );
+ }
return pvec;
}
zypp::url::ParamMap
UrlBase::getQueryStringMap(EEncoding eflag) const
{
+ if( config("psep_querystr").empty() ||
+ config("vsep_querystr").empty())
+ {
+ ZYPP_THROW(UrlNotSupportedException(
+ _("Query string parsing not supported for this URL")
+ ));
+ }
zypp::url::ParamMap pmap;
zypp::url::split(
pmap,
// ---------------------------------------------------------------
+ std::string
+ UrlBase::getQueryParam(const std::string ¶m, EEncoding eflag) const
+ {
+ zypp::url::ParamMap pmap( getQueryStringMap( eflag));
+ zypp::url::ParamMap::const_iterator i( pmap.find(param));
+
+ return i != pmap.end() ? i->second : std::string();
+ }
+
+
+ // ---------------------------------------------------------------
void
UrlBase::setScheme(const std::string &scheme)
{
if( isValidScheme(scheme))
{
- m_data->scheme = scheme;
+ m_data->scheme = str::toLower(scheme);
}
else
+ if( scheme.empty())
{
- throw std::invalid_argument(
- std::string("Invalid Url scheme '" + scheme + "'")
- );
+ ZYPP_THROW(UrlBadComponentException(
+ _("Url scheme is a required component")
+ ));
+ }
+ else
+ {
+ ZYPP_THROW(UrlBadComponentException(
+ str::form(_("Invalid Url scheme '%s'"), scheme.c_str())
+ ));
}
}
void
UrlBase::setAuthority(const std::string &authority)
{
- str::regex rex(RX_SPLIT_AUTHORITY);
- str::smatch out;
- bool ret = str::regex_match(authority, out, rex);
+ std::string s = authority;
+ std::string::size_type p,q;
- // FIXME:
- #if defined(WITH_URL_PARSE_DEBUG)
- std::cerr << "Regex parsed URL authority("
- << out.size() << "):" << std::endl;
- std::cerr << "==> " << authority << std::endl;
- for(size_t n=0; n < out.size(); n++)
- {
- std::cerr << "[" << n << "] " << out[n].str() << std::endl;
- }
- #endif
+ std::string username, password, host, port;
- if( ret && out.size() == 8)
+ if ((p=s.find('@')) != std::string::npos)
{
- setUsername(out[2].str());
- setPassword(out[4].str());
- setHost(out[5].str());
- setPort(out[7].str());
+ q = s.find(':');
+ if (q != std::string::npos && q < p)
+ {
+ setUsername(s.substr(0, q), zypp::url::E_ENCODED);
+ setPassword(s.substr(q+1, p-q-1), zypp::url::E_ENCODED);
+ }
+ else
+ setUsername(s.substr(0, p), zypp::url::E_ENCODED);
+ s = s.substr(p+1);
}
- else
+ if ((p = s.rfind(':')) != std::string::npos && ( (q = s.rfind(']')) == std::string::npos || q < p) )
{
- throw std::invalid_argument(
- "Unable to parse Url authority"
- );
+ setHost(s.substr(0, p));
+ setPort(s.substr(p+1));
}
+ else
+ setHost(s);
}
-
// ---------------------------------------------------------------
void
UrlBase::setPathData(const std::string &pathdata)
{
- size_t pos;
- pos = pathdata.find(config("sep_pathparams"));
+ size_t pos = std::string::npos;
+ std::string sep(config("sep_pathparams"));
+
+ if( !sep.empty())
+ pos = pathdata.find(sep);
+
if( pos != std::string::npos)
{
- setPathName(pathdata.substr(0, pos));
+ setPathName(pathdata.substr(0, pos),
+ zypp::url::E_ENCODED);
setPathParams(pathdata.substr(pos + 1));
}
else
{
- setPathName(pathdata);
+ setPathName(pathdata,
+ zypp::url::E_ENCODED);
setPathParams("");
}
}
{
checkUrlData(querystr, "query string", config("rx_querystr"));
- // FIXME: split & recode?
m_data->querystr = querystr;
}
}
// ---------------------------------------------------------------
void
- UrlBase::setFragment(const std::string &fragment)
+ UrlBase::setFragment(const std::string &fragment,
+ EEncoding eflag)
{
if( fragment.empty())
{
}
else
{
- std::string data( zypp::url::decode(fragment));
-
- checkUrlData(data, "fragment", config("rx_fragment"));
+ if(eflag == zypp::url::E_ENCODED)
+ {
+ checkUrlData(fragment, "fragment", config("rx_fragment"));
- m_data->fragment = zypp::url::encode(
- data, config("safe_fragment")
- );
+ m_data->fragment = fragment;
+ }
+ else
+ {
+ m_data->fragment = zypp::url::encode(
+ fragment, config("safe_fragment")
+ );
+ }
}
}
// ---------------------------------------------------------------
void
- UrlBase::setUsername(const std::string &user)
+ UrlBase::setUsername(const std::string &user,
+ EEncoding eflag)
{
if( user.empty())
{
}
else
{
- std::string data( zypp::url::decode(user));
+ if( config("with_authority") != "y")
+ {
+ ZYPP_THROW(UrlNotAllowedException(
+ _("Url scheme does not allow a username")
+ ));
+ }
- checkUrlData(data, "username", config("rx_username"));
+ if(eflag == zypp::url::E_ENCODED)
+ {
+ checkUrlData(user, "username", config("rx_username"));
- m_data->user = zypp::url::encode(
- data, config("safe_username")
- );
+ m_data->user = user;
+ }
+ else
+ {
+ m_data->user = zypp::url::encode(
+ user, config("safe_username")
+ );
+ }
}
}
// ---------------------------------------------------------------
void
- UrlBase::setPassword(const std::string &pass)
+ UrlBase::setPassword(const std::string &pass,
+ EEncoding eflag)
{
if( pass.empty())
{
}
else
{
- std::string data( zypp::url::decode(pass));
+ if( config("with_authority") != "y")
+ {
+ ZYPP_THROW(UrlNotAllowedException(
+ _("Url scheme does not allow a password")
+ ));
+ }
- checkUrlData(data, "password", config("rx_password"));
+ if(eflag == zypp::url::E_ENCODED)
+ {
+ checkUrlData(pass, "password", config("rx_password"), false);
- m_data->pass = zypp::url::encode(
- data, config("safe_password")
- );
+ m_data->pass = pass;
+ }
+ else
+ {
+ m_data->pass = zypp::url::encode(
+ pass, config("safe_password")
+ );
+ }
}
}
{
if( host.empty())
{
+ if(config("require_host") == "m")
+ {
+ ZYPP_THROW(UrlNotAllowedException(
+ _("Url scheme requires a host component")
+ ));
+ }
m_data->host = host;
}
else
{
- std::string data( zypp::url::decode(host));
+ if( config("with_authority") != "y")
+ {
+ ZYPP_THROW(UrlNotAllowedException(
+ _("Url scheme does not allow a host component")
+ ));
+ }
- checkUrlData(data, "hostname", config("rx_hostname"));
+ if( isValidHost(host))
+ {
+ std::string temp;
- m_data->host = zypp::url::encode(
- data, config("safe_hostname")
- );
+ // always decode in case isValidHost()
+ // is reimplemented and supports also
+ // the [v ... ] notation.
+ if( host.at(0) == '[')
+ {
+ temp = str::toUpper(zypp::url::decode(host));
+ }
+ else
+ {
+ temp = str::toLower(zypp::url::decode(host));
+ }
+
+ m_data->host = zypp::url::encode(
+ temp, config("safe_hostname")
+ );
+ }
+ else
+ {
+ ZYPP_THROW(UrlBadComponentException(
+ str::form(_("Invalid host component '%s'"), host.c_str())
+ ));
+ }
}
}
}
else
{
- std::string data( zypp::url::decode(port));
-
- checkUrlData(data, "port", config("rx_port"));
+ if( config("with_authority") != "y" ||
+ config("with_port") != "y")
+ {
+ ZYPP_THROW(UrlNotAllowedException(
+ _("Url scheme does not allow a port")
+ ));
+ }
- m_data->port = data;
+ if( isValidPort(port))
+ {
+ m_data->port = port;
+ }
+ else
+ {
+ ZYPP_THROW(UrlBadComponentException(
+ str::form(_("Invalid port component '%s'"), port.c_str())
+ ));
+ }
}
}
// ---------------------------------------------------------------
void
- UrlBase::setPathName(const std::string &path)
+ UrlBase::setPathName(const std::string &path,
+ EEncoding eflag)
{
if( path.empty())
{
+ if(config("require_pathname") == "m")
+ {
+ ZYPP_THROW(UrlNotAllowedException(
+ _("Url scheme requires path name")
+ ));
+ }
m_data->pathname = path;
}
else
{
- std::string data( zypp::url::decode(path));
+ if(eflag == zypp::url::E_ENCODED)
+ {
+ checkUrlData(path, "path name", config("rx_pathname"));
- checkUrlData(data, "path", config("rx_pathname"));
+ if( !getHost(zypp::url::E_ENCODED).empty())
+ {
+ // has to begin with a "/". For consistency with
+ // setPathName while the host is empty, we allow
+ // it in encoded ("%2f") form - cleanupPathName()
+ // will fix / decode the first slash if needed.
+ if(!(path.at(0) == '/' || (path.size() >= 3 &&
+ str::toLower(path.substr(0, 3)) == "%2f")))
+ {
+ ZYPP_THROW(UrlNotAllowedException(
+ _("Relative path not allowed if authority exists")
+ ));
+ }
+ }
- m_data->pathname = zypp::url::encode(
- data, config("safe_pathname")
- );
+ m_data->pathname = cleanupPathName(path);
+ }
+ else // zypp::url::E_DECODED
+ {
+ if( !getHost(zypp::url::E_ENCODED).empty())
+ {
+ if(path.at(0) != '/')
+ {
+ ZYPP_THROW(UrlNotAllowedException(
+ _("Relative path not allowed if authority exists")
+ ));
+ }
+ }
+
+ m_data->pathname = cleanupPathName(
+ zypp::url::encode(
+ path, config("safe_pathname")
+ )
+ );
+ }
}
}
{
checkUrlData(params, "path parameters", config("rx_pathparams"));
- // FIXME: split & recode?
m_data->pathparams = params;
}
}
void
UrlBase::setPathParamsMap(const zypp::url::ParamMap &pmap)
{
+ if( config("psep_pathparam").empty() ||
+ config("vsep_pathparam").empty())
+ {
+ ZYPP_THROW(UrlNotSupportedException(
+ "Path Parameter parsing not supported for this URL"
+ ));
+ }
setPathParams(
zypp::url::join(
pmap,
// ---------------------------------------------------------------
void
+ UrlBase::setPathParam(const std::string ¶m, const std::string &value)
+ {
+ zypp::url::ParamMap pmap( getPathParamsMap(zypp::url::E_DECODED));
+ pmap[param] = value;
+ setPathParamsMap(pmap);
+ }
+
+
+ // ---------------------------------------------------------------
+ void
UrlBase::setQueryStringVec(const zypp::url::ParamVec &pvec)
{
setQueryString(
void
UrlBase::setQueryStringMap(const zypp::url::ParamMap &pmap)
{
+ if( config("psep_querystr").empty() ||
+ config("vsep_querystr").empty())
+ {
+ ZYPP_THROW(UrlNotSupportedException(
+ _("Query string parsing not supported for this URL")
+ ));
+ }
setQueryString(
zypp::url::join(
pmap,
);
}
+ // ---------------------------------------------------------------
+ void
+ UrlBase::setQueryParam(const std::string ¶m, const std::string &value)
+ {
+ zypp::url::ParamMap pmap( getQueryStringMap(zypp::url::E_DECODED));
+ pmap[param] = value;
+ setQueryStringMap(pmap);
+ }
+
+ // ---------------------------------------------------------------
+ void
+ UrlBase::delQueryParam(const std::string ¶m)
+ {
+ zypp::url::ParamMap pmap( getQueryStringMap(zypp::url::E_DECODED));
+ pmap.erase(param);
+ setQueryStringMap(pmap);
+ }
+
+
+ // ---------------------------------------------------------------
+ std::string
+ UrlBase::cleanupPathName(const std::string &path) const
+ {
+ bool authority = !getHost(zypp::url::E_ENCODED).empty();
+ return cleanupPathName(path, authority);
+ }
+
+ // ---------------------------------------------------------------
+ std::string
+ UrlBase::cleanupPathName(const std::string &path, bool authority) const
+ {
+ std::string copy( path);
+
+ // decode the first slash if it is encoded ...
+ if(copy.size() >= 3 && copy.at(0) != '/' &&
+ str::toLower(copy.substr(0, 3)) == "%2f")
+ {
+ copy.replace(0, 3, "/");
+ }
+
+ // if path begins with a double slash ("//"); encode the second
+ // slash [minimal and IMO sufficient] before the first path
+ // segment, to fulfill the path-absolute rule of RFC 3986
+ // disallowing a "//" if no authority is present.
+ if( authority)
+ {
+ //
+ // rewrite of "//" to "/%2f" not required, use config
+ //
+ if(config("path_encode_slash2") == "y")
+ {
+ // rewrite "//" ==> "/%2f"
+ if(copy.size() >= 2 && copy.at(0) == '/' && copy.at(1) == '/')
+ {
+ copy.replace(1, 1, "%2F");
+ }
+ }
+ else
+ {
+ // rewrite "/%2f" ==> "//"
+ if(copy.size() >= 4 && copy.at(0) == '/' &&
+ str::toLower(copy.substr(1, 4)) == "%2f")
+ {
+ copy.replace(1, 4, "/");
+ }
+ }
+ }
+ else
+ {
+ // rewrite of "//" to "/%2f" is required (no authority)
+ if(copy.size() >= 2 && copy.at(0) == '/' && copy.at(1) == '/')
+ {
+ copy.replace(1, 1, "%2F");
+ }
+ }
+ return copy;
+ }
+
+
+ // ---------------------------------------------------------------
+ bool
+ UrlBase::isValidHost(const std::string &host) const
+ {
+ try
+ {
+ str::regex regx(RX_VALID_HOSTIPV6);
+ if( str::regex_match(host, regx))
+ {
+ struct in6_addr ip;
+ std::string temp( host.substr(1, host.size()-2));
+
+ return inet_pton(AF_INET6, temp.c_str(), &ip) > 0;
+ }
+ else
+ {
+ // matches also IPv4 dotted-decimal adresses...
+ std::string temp( zypp::url::decode(host));
+ str::regex regx(RX_VALID_HOSTNAME);
+ return str::regex_match(temp, regx);
+ }
+ }
+ catch( ... )
+ {}
+
+ return false;
+ }
+
+
+ // ---------------------------------------------------------------
+ bool
+ UrlBase::isValidPort(const std::string &port) const
+ {
+ try
+ {
+ str::regex regx(RX_VALID_PORT);
+ if( str::regex_match(port, regx))
+ {
+ long pnum = str::strtonum<long>(port);
+ return ( pnum >= 1 && pnum <= USHRT_MAX);
+ }
+ }
+ catch( ... )
+ {}
+ return false;
+ }
+
//////////////////////////////////////////////////////////////////
} // namespace url