1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
10 * \file zypp/url/UrlBase.cc
12 #include <zypp/url/UrlBase.h>
13 #include <zypp/base/String.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <arpa/inet.h>
23 // ---------------------------------------------------------------
25 ** authority = //[user [:password] @ ] host [:port]
27 ** host = hostname | IPv4 | "[" IPv6-IP "]" | "[v...]"
29 #define RX_SPLIT_AUTHORITY \
30 "^(([^:@]*)([:]([^@]*))?@)?(\\[[^]]+\\]|[^:]+)?([:](.*))?"
32 #define RX_VALID_SCHEME "^[a-zA-Z][a-zA-Z0-9\\.+-]*$"
34 #define RX_VALID_PORT "^[0-9]{1,5}$"
36 #define RX_VALID_HOSTNAME "^[[:alnum:]]+([\\.-][[:alnum:]]+)*$"
38 #define RX_VALID_HOSTIPV4 \
39 "^([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$"
41 #define RX_VALID_HOSTIPV6 \
42 "^\\[[:a-fA-F0-9]+(:[0-9]{1,3}(\\.[0-9]{1,3}){3})?\\]$"
45 //////////////////////////////////////////////////////////////////////
47 { ////////////////////////////////////////////////////////////////////
49 ////////////////////////////////////////////////////////////////////
51 { //////////////////////////////////////////////////////////////////
54 // ---------------------------------------------------------------
56 ** URL asString() view option constants:
58 const ViewOptions ViewOptions::WITH_SCHEME = 0x0001;
59 const ViewOptions ViewOptions::WITH_USERNAME = 0x0002;
60 const ViewOptions ViewOptions::WITH_PASSWORD = 0x0004;
61 const ViewOptions ViewOptions::WITH_HOST = 0x0008;
62 const ViewOptions ViewOptions::WITH_PORT = 0x0010;
63 const ViewOptions ViewOptions::WITH_PATH_NAME = 0x0020;
64 const ViewOptions ViewOptions::WITH_PATH_PARAMS = 0x0040;
65 const ViewOptions ViewOptions::WITH_QUERY_STR = 0x0080;
66 const ViewOptions ViewOptions::WITH_FRAGMENT = 0x0100;
67 const ViewOptions ViewOptions::EMPTY_AUTHORITY = 0x0200;
68 const ViewOptions ViewOptions::EMPTY_PATH_NAME = 0x0400;
69 const ViewOptions ViewOptions::EMPTY_PATH_PARAMS = 0x0800;
70 const ViewOptions ViewOptions::EMPTY_QUERY_STR = 0x1000;
71 const ViewOptions ViewOptions::EMPTY_FRAGMENT = 0x2000;
72 const ViewOptions ViewOptions::DEFAULTS = 0x07bb;
74 ViewOptions::WITH_SCHEME +
75 ViewOptions::WITH_USERNAME +
76 ViewOptions::WITH_HOST +
77 ViewOptions::WITH_PORT +
78 ViewOptions::WITH_PATH_NAME +
79 ViewOptions::WITH_QUERY_STR +
80 ViewOptions::WITH_FRAGMENT +
81 ViewOptions::EMPTY_AUTHORITY +
82 ViewOptions::EMPTY_PATH_NAME;
86 // ---------------------------------------------------------------
88 ** Behaviour configuration variables.
90 typedef std::map< std::string, std::string > UrlConfig;
93 // ---------------------------------------------------------------
95 * \brief Internal data used by UrlBase.
103 UrlBaseData(const UrlConfig &conf)
115 std::string pathname;
116 std::string pathparams;
117 std::string querystr;
118 std::string fragment;
122 // ---------------------------------------------------------------
124 ** Anonymous/internal utility namespace:
126 namespace // anonymous
129 // -------------------------------------------------------------
131 checkUrlData(const std::string &data,
132 const std::string &name,
133 const std::string ®x)
135 if( regx.empty() || regx == "^$")
137 throw std::invalid_argument(
138 std::string("Url scheme does not allow a " +
147 str::regex rex(regx);
148 valid = str::regex_match(data, rex);
155 throw std::invalid_argument(
156 std::string("Invalid " + name + " argument '" +
166 // ---------------------------------------------------------------
174 // ---------------------------------------------------------------
176 : m_data( new UrlBaseData())
182 // ---------------------------------------------------------------
183 UrlBase::UrlBase(const UrlBase &url)
184 : m_data( new UrlBaseData( *(url.m_data)))
189 // ---------------------------------------------------------------
190 UrlBase::UrlBase(const std::string &scheme,
191 const std::string &authority,
192 const std::string &pathdata,
193 const std::string &querystr,
194 const std::string &fragment)
195 : m_data( new UrlBaseData())
198 init(scheme, authority, pathdata, querystr, fragment);
202 // ---------------------------------------------------------------
204 UrlBase::init(const std::string &scheme,
205 const std::string &authority,
206 const std::string &pathdata,
207 const std::string &querystr,
208 const std::string &fragment)
211 setAuthority(authority);
212 setPathData(pathdata);
213 setQueryString(querystr);
214 setFragment(fragment, zypp::url::E_ENCODED);
218 // ---------------------------------------------------------------
222 config("sep_pathparams", ";");
223 config("psep_pathparam", ",");
224 config("vsep_pathparam", "=");
226 config("psep_querystr", "&");
227 config("vsep_querystr", "=");
229 config("safe_username", "~!$&'()*+=,;");
230 config("safe_password", "~!$&'()*+=,:;");
231 config("safe_hostname", "[:]");
232 config("safe_pathname", "~!$&'()*+=,:@/");
233 config("safe_pathparams", "~!$&'()*+=,:;@/");
234 config("safe_querystr", "~!$&'()*+=,:;@/?");
235 config("safe_fragment", "~!$&'()*+=,:;@/?");
237 config("with_authority", "y");
239 config("rx_username", "^([a-zA-Z0-9!$&'\\(\\)*+=,;~\\._-]|%[a-fA-F0-9]{2})+$");
240 config("rx_password", "^([a-zA-Z0-9!$&'\\(\\)*+=,:;~\\._-]|%[a-fA-F0-9]{2})+$");
242 config("rx_pathname", "^([a-zA-Z0-9!$&'\\(\\)*+=,:@/~\\._-]|%[a-fA-F0-9]{2})+$");
243 config("rx_pathparams", "^([a-zA-Z0-9!$&'\\(\\)*+=,:;@/~\\._-]|%[a-fA-F0-9]{2})+$");
245 config("rx_querystr", "^([a-zA-Z0-9!$&'\\(\\)*+=,:;@/?~\\._-]|%[a-fA-F0-9]{2})+$");
246 config("rx_fragment", "^([a-zA-Z0-9!$&'\\(\\)*+=,:;@/?~\\._-]|%[a-fA-F0-9]{2})+$");
250 // ---------------------------------------------------------------
252 UrlBase::config(const std::string &opt, const std::string &val)
254 m_data->config[opt] = val;
258 // ---------------------------------------------------------------
260 UrlBase::config(const std::string &opt) const
262 UrlConfig::const_iterator v( m_data->config.find(opt));
263 if( v != m_data->config.end())
266 return std::string();
270 // ---------------------------------------------------------------
272 UrlBase::getViewOptions() const
274 return m_data->vopts;
278 // ---------------------------------------------------------------
280 UrlBase::setViewOptions(const ViewOptions &vopts)
282 m_data->vopts = vopts;
286 // ---------------------------------------------------------------
290 zypp::url::UrlConfig config(m_data->config);
291 zypp::url::ViewOptions vopts(m_data->vopts);
292 *m_data = UrlBaseData();
293 m_data->config = config;
294 m_data->vopts = vopts;
298 // ---------------------------------------------------------------
300 UrlBase::clone() const
302 return new UrlBase(*this);
306 // ---------------------------------------------------------------
307 zypp::url::UrlSchemes
308 UrlBase::getKnownSchemes() const
314 // ---------------------------------------------------------------
316 UrlBase::isKnownScheme(const std::string &scheme) const
318 std::string lscheme( str::toLower(scheme));
319 UrlSchemes schemes( getKnownSchemes());
320 UrlSchemes::const_iterator s;
322 for(s=schemes.begin(); s!=schemes.end(); ++s)
324 if( lscheme == str::toLower(*s))
331 // ---------------------------------------------------------------
333 UrlBase::isValidScheme(const std::string &scheme) const
338 str::regex rex(RX_VALID_SCHEME);
339 valid = str::regex_match(scheme, rex);
344 if(scheme.empty() || valid)
346 std::string lscheme( str::toLower(scheme));
347 UrlSchemes schemes( getKnownSchemes());
352 UrlSchemes::const_iterator s;
353 for(s=schemes.begin(); s!=schemes.end(); ++s)
355 if( lscheme == str::toLower(*s))
363 // ---------------------------------------------------------------
365 UrlBase::isValid() const
367 return !getScheme().empty();
371 // ---------------------------------------------------------------
373 UrlBase::asString() const
375 return asString(getViewOptions());
379 // ---------------------------------------------------------------
381 UrlBase::asString(const zypp::url::ViewOptions &opts) const
386 if( opts.has(ViewOptions::WITH_SCHEME))
388 tmp.scheme = getScheme();
389 if( !tmp.scheme.empty())
391 url += tmp.scheme + ":";
393 if( opts.has(ViewOptions::WITH_HOST))
395 tmp.host = getHost(zypp::url::E_ENCODED);
396 if( !tmp.host.empty())
400 if( opts.has(ViewOptions::WITH_USERNAME))
402 tmp.user = getUsername(zypp::url::E_ENCODED);
403 if( !tmp.user.empty())
407 if( opts.has(ViewOptions::WITH_PASSWORD))
409 tmp.pass = getPassword(zypp::url::E_ENCODED);
410 if( !tmp.pass.empty())
412 url += ":" + tmp.pass;
421 if( opts.has(ViewOptions::WITH_PORT))
423 tmp.port = getPort();
424 if( !tmp.port.empty())
426 url += ":" + tmp.port;
431 else if( opts.has(ViewOptions::EMPTY_AUTHORITY))
438 if( opts.has(ViewOptions::WITH_PATH_NAME))
440 tmp.pathname = getPathName(zypp::url::E_ENCODED);
441 if( !tmp.pathname.empty())
443 if( (!tmp.host.empty() || opts.has(ViewOptions::EMPTY_AUTHORITY))
444 && (tmp.pathname.at(0) != '/'))
450 if( opts.has(ViewOptions::WITH_PATH_PARAMS))
452 tmp.pathparams = getPathParams();
453 if( !tmp.pathparams.empty())
455 url += ";" + tmp.pathparams;
457 else if( opts.has(ViewOptions::EMPTY_PATH_PARAMS))
463 else if( opts.has(ViewOptions::EMPTY_PATH_NAME))
466 if( opts.has(ViewOptions::EMPTY_PATH_PARAMS))
473 if( opts.has(ViewOptions::WITH_QUERY_STR))
475 tmp.querystr = getQueryString();
476 if( !tmp.querystr.empty())
478 url += "?" + tmp.querystr;
480 else if( opts.has(ViewOptions::EMPTY_QUERY_STR))
486 if( opts.has(ViewOptions::WITH_FRAGMENT))
488 tmp.fragment = getFragment(zypp::url::E_ENCODED);
489 if( !tmp.fragment.empty())
491 url += "#" + tmp.fragment;
493 else if( opts.has(ViewOptions::EMPTY_FRAGMENT))
503 // ---------------------------------------------------------------
505 UrlBase::getScheme() const
507 return m_data->scheme;
511 // ---------------------------------------------------------------
513 UrlBase::getAuthority() const
516 if( !getHost(zypp::url::E_ENCODED).empty())
518 if( !getUsername(zypp::url::E_ENCODED).empty())
520 str = getUsername(zypp::url::E_ENCODED);
521 if( !getPassword(zypp::url::E_ENCODED).empty())
523 str += ":" + getPassword(zypp::url::E_ENCODED);
528 str += getHost(zypp::url::E_ENCODED);
529 if( !getPort().empty())
531 str += ":" + getPort();
538 // ---------------------------------------------------------------
540 UrlBase::getPathData() const
542 return getPathName(zypp::url::E_ENCODED) +
543 config("sep_pathparams") +
548 // ---------------------------------------------------------------
550 UrlBase::getQueryString() const
552 return m_data->querystr;
556 // ---------------------------------------------------------------
558 UrlBase::getFragment(EEncoding eflag) const
560 if(eflag == zypp::url::E_DECODED)
561 return zypp::url::decode(m_data->fragment);
563 return m_data->fragment;
567 // ---------------------------------------------------------------
569 UrlBase::getUsername(EEncoding eflag) const
571 if(eflag == zypp::url::E_DECODED)
572 return zypp::url::decode(m_data->user);
578 // ---------------------------------------------------------------
580 UrlBase::getPassword(EEncoding eflag) const
582 if(eflag == zypp::url::E_DECODED)
583 return zypp::url::decode(m_data->pass);
589 // ---------------------------------------------------------------
591 UrlBase::getHost(EEncoding eflag) const
593 if(eflag == zypp::url::E_DECODED)
594 return zypp::url::decode(m_data->host);
600 // ---------------------------------------------------------------
602 UrlBase::getPort() const
608 // ---------------------------------------------------------------
610 UrlBase::getPathName(EEncoding eflag) const
612 if(eflag == zypp::url::E_DECODED)
613 return zypp::url::decode(m_data->pathname);
615 return m_data->pathname;
619 // ---------------------------------------------------------------
621 UrlBase::getPathParams() const
623 return m_data->pathparams;
627 // ---------------------------------------------------------------
629 UrlBase::getPathParamsVec() const
631 zypp::url::ParamVec pvec;
632 if( config("psep_pathparam").empty())
634 pvec.push_back(getPathParams());
641 config("psep_pathparam")
648 // ---------------------------------------------------------------
650 UrlBase::getPathParamsMap(EEncoding eflag) const
652 if( config("psep_pathparam").empty() ||
653 config("vsep_pathparam").empty())
655 throw std::logic_error(
656 "Path parameter parsing not supported for this URL"
659 zypp::url::ParamMap pmap;
663 config("psep_pathparam"),
664 config("vsep_pathparam"),
671 // ---------------------------------------------------------------
673 UrlBase::getPathParam(const std::string ¶m, EEncoding eflag) const
675 zypp::url::ParamMap pmap( getPathParamsMap( eflag));
676 zypp::url::ParamMap::const_iterator i( pmap.find(param));
678 return i != pmap.end() ? i->second : std::string();
682 // ---------------------------------------------------------------
684 UrlBase::getQueryStringVec() const
686 zypp::url::ParamVec pvec;
687 if( config("psep_querystr").empty())
689 pvec.push_back(getQueryString());
696 config("psep_querystr")
703 // ---------------------------------------------------------------
705 UrlBase::getQueryStringMap(EEncoding eflag) const
707 if( config("psep_querystr").empty() ||
708 config("vsep_querystr").empty())
710 throw std::logic_error(
711 "Query string parsing not supported for this URL"
714 zypp::url::ParamMap pmap;
718 config("psep_querystr"),
719 config("vsep_querystr"),
726 // ---------------------------------------------------------------
728 UrlBase::getQueryParam(const std::string ¶m, EEncoding eflag) const
730 zypp::url::ParamMap pmap( getQueryStringMap( eflag));
731 zypp::url::ParamMap::const_iterator i( pmap.find(param));
733 return i != pmap.end() ? i->second : std::string();
737 // ---------------------------------------------------------------
739 UrlBase::setScheme(const std::string &scheme)
741 if( isValidScheme(scheme))
743 m_data->scheme = str::toLower(scheme);
747 throw std::invalid_argument(
748 std::string("Invalid Url scheme '" + scheme + "'")
754 // ---------------------------------------------------------------
756 UrlBase::setAuthority(const std::string &authority)
763 str::regex rex(RX_SPLIT_AUTHORITY);
764 ret = str::regex_match(authority, out, rex);
769 if( ret && out.size() == 8)
771 setUsername(out[2].str(), zypp::url::E_ENCODED);
772 setPassword(out[4].str(), zypp::url::E_ENCODED);
773 setHost(out[5].str());
774 setPort(out[7].str());
778 throw std::invalid_argument(
779 "Unable to parse Url authority"
784 // ---------------------------------------------------------------
786 UrlBase::setPathData(const std::string &pathdata)
788 size_t pos = std::string::npos;
789 std::string sep(config("sep_pathparams"));
792 pos = pathdata.find(sep);
794 if( pos != std::string::npos)
796 setPathName(pathdata.substr(0, pos),
797 zypp::url::E_ENCODED);
798 setPathParams(pathdata.substr(pos + 1));
802 setPathName(pathdata,
803 zypp::url::E_ENCODED);
809 // ---------------------------------------------------------------
811 UrlBase::setQueryString(const std::string &querystr)
813 if( querystr.empty())
815 m_data->querystr = querystr;
819 checkUrlData(querystr, "query string", config("rx_querystr"));
821 m_data->querystr = querystr;
826 // ---------------------------------------------------------------
828 UrlBase::setFragment(const std::string &fragment,
831 if( fragment.empty())
833 m_data->fragment = fragment;
837 if(eflag == zypp::url::E_ENCODED)
839 checkUrlData(fragment, "fragment", config("rx_fragment"));
841 m_data->fragment = fragment;
845 m_data->fragment = zypp::url::encode(
846 fragment, config("safe_password")
853 // ---------------------------------------------------------------
855 UrlBase::setUsername(const std::string &user,
864 if( config("with_authority") != "y")
866 throw std::invalid_argument(
867 std::string("Url scheme does not allow a username")
871 if(eflag == zypp::url::E_ENCODED)
873 checkUrlData(user, "username", config("rx_username"));
879 m_data->user = zypp::url::encode(
880 user, config("safe_username")
887 // ---------------------------------------------------------------
889 UrlBase::setPassword(const std::string &pass,
898 if( config("with_authority") != "y")
900 throw std::invalid_argument(
901 std::string("Url scheme does not allow a password")
905 if(eflag == zypp::url::E_ENCODED)
907 checkUrlData(pass, "password", config("rx_password"));
913 m_data->pass = zypp::url::encode(
914 pass, config("safe_password")
921 // ---------------------------------------------------------------
923 UrlBase::setHost(const std::string &host)
931 if( config("with_authority") != "y")
933 throw std::invalid_argument(
934 std::string("Url scheme does not allow a host")
938 if( isValidHost(host))
942 // always decode in case isValidHost()
943 // is reimplemented and supports also
944 // the [v ... ] notation.
945 if( host.at(0) == '[')
947 temp = str::toUpper(zypp::url::decode(host));
951 temp = str::toLower(zypp::url::decode(host));
954 m_data->host = zypp::url::encode(
955 temp, config("safe_hostname")
960 throw std::invalid_argument(
961 std::string("Invalid host argument '" + host + "'")
968 // ---------------------------------------------------------------
970 UrlBase::setPort(const std::string &port)
978 if( config("with_authority") != "y")
980 throw std::invalid_argument(
981 std::string("Url scheme does not allow a port")
985 if( isValidPort(port))
991 throw std::invalid_argument(
992 std::string("Invalid host argument '" + port + "'")
999 // ---------------------------------------------------------------
1001 UrlBase::setPathName(const std::string &path,
1006 m_data->pathname = path;
1011 if(eflag == zypp::url::E_ENCODED)
1013 checkUrlData(path, "path name", config("rx_pathname"));
1015 data = cleanupPathName(zypp::url::decode(path));
1019 data = cleanupPathName(path);
1022 m_data->pathname = zypp::url::encode(
1023 data, config("safe_pathname")
1029 // ---------------------------------------------------------------
1031 UrlBase::setPathParams(const std::string ¶ms)
1035 m_data->pathparams = params;
1039 checkUrlData(params, "path parameters", config("rx_pathparams"));
1041 m_data->pathparams = params;
1046 // ---------------------------------------------------------------
1048 UrlBase::setPathParamsVec(const zypp::url::ParamVec &pvec)
1053 config("psep_pathparam")
1059 // ---------------------------------------------------------------
1061 UrlBase::setPathParamsMap(const zypp::url::ParamMap &pmap)
1063 if( config("psep_pathparam").empty() ||
1064 config("vsep_pathparam").empty())
1066 throw std::logic_error(
1067 "Path Parameter parsing not supported for this URL"
1073 config("psep_pathparam"),
1074 config("vsep_pathparam"),
1075 config("safe_pathparams")
1081 // ---------------------------------------------------------------
1083 UrlBase::setPathParam(const std::string ¶m, const std::string &value)
1085 zypp::url::ParamMap pmap( getPathParamsMap(zypp::url::E_DECODED));
1086 pmap[param] = value;
1087 setPathParamsMap(pmap);
1091 // ---------------------------------------------------------------
1093 UrlBase::setQueryStringVec(const zypp::url::ParamVec &pvec)
1098 config("psep_querystr")
1104 // ---------------------------------------------------------------
1106 UrlBase::setQueryStringMap(const zypp::url::ParamMap &pmap)
1108 if( config("psep_querystr").empty() ||
1109 config("vsep_querystr").empty())
1111 throw std::logic_error(
1112 "Query string parsing not supported for this URL"
1118 config("psep_querystr"),
1119 config("vsep_querystr"),
1120 config("safe_querystr")
1125 // ---------------------------------------------------------------
1127 UrlBase::setQueryParam(const std::string ¶m, const std::string &value)
1129 zypp::url::ParamMap pmap( getQueryStringMap(zypp::url::E_DECODED));
1130 pmap[param] = value;
1131 setQueryStringMap(pmap);
1135 // ---------------------------------------------------------------
1137 UrlBase::cleanupPathName(const std::string &path)
1141 while( pos < path.length() && path.at(pos) == '/')
1146 // make sure, there is not more than
1147 // _one_ leading "/" in the path name.
1148 return path.substr(pos - 1);
1151 return std::string(path);
1155 // ---------------------------------------------------------------
1157 UrlBase::isValidHost(const std::string &host)
1161 str::regex regx(RX_VALID_HOSTIPV6);
1162 if( str::regex_match(host, regx))
1165 std::string temp( host.substr(1, host.size()-2));
1167 return inet_pton(AF_INET6, temp.c_str(), &ip) > 0;
1171 // matches also IPv4 dotted-decimal adresses...
1172 std::string temp( zypp::url::decode(host));
1173 str::regex regx(RX_VALID_HOSTNAME);
1174 return str::regex_match(temp, regx);
1184 // ---------------------------------------------------------------
1186 UrlBase::isValidPort(const std::string &port)
1190 str::regex regx(RX_VALID_PORT);
1191 if( str::regex_match(port, regx))
1193 long pnum = str::strtonum<long>(port);
1194 return ( pnum >= 1 && pnum <= USHRT_MAX);
1204 //////////////////////////////////////////////////////////////////
1206 ////////////////////////////////////////////////////////////////////
1208 ////////////////////////////////////////////////////////////////////
1210 //////////////////////////////////////////////////////////////////////
1212 ** vim: set ts=2 sts=2 sw=2 ai et: