Imported Upstream version 15.0.0
[platform/upstream/libzypp.git] / zypp / url / UrlBase.cc
index 9c9a001..5238d52 100644 (file)
@@ -9,14 +9,42 @@
 /**
  * \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
@@ -28,68 +56,131 @@ namespace zypp
 
 
     // ---------------------------------------------------------------
-    const ViewOptions ViewOptions::WITH_SCHEME       = 0x0001;
-    const ViewOptions ViewOptions::WITH_USERNAME     = 0x0002;
-    const ViewOptions ViewOptions::WITH_PASSWORD     = 0x0004;
-    const ViewOptions ViewOptions::WITH_HOST         = 0x0008;
-    const ViewOptions ViewOptions::WITH_PORT         = 0x0010;
-    const ViewOptions ViewOptions::WITH_PATH_NAME    = 0x0020;
-    const ViewOptions ViewOptions::WITH_PATH_PARAMS  = 0x0040;
-    const ViewOptions ViewOptions::WITH_QUERY_STR    = 0x0080;
-    const ViewOptions ViewOptions::WITH_FRAGMENT     = 0x0100;
-    const ViewOptions ViewOptions::EMPTY_AUTHORITY   = 0x0200;
-    const ViewOptions ViewOptions::EMPTY_PATH_NAME   = 0x0400;
-    const ViewOptions ViewOptions::EMPTY_PATH_PARAMS = 0x0800;
-    const ViewOptions ViewOptions::EMPTY_QUERY_STR   = 0x1000;
-    const ViewOptions ViewOptions::EMPTY_FRAGMENT    = 0x2000;
-    const ViewOptions ViewOptions::DEFAULTS          = 0x07bb;
     /*
-                      ViewOptions::WITH_SCHEME       +
-                      ViewOptions::WITH_USERNAME     +
-                      ViewOptions::WITH_HOST         +
-                      ViewOptions::WITH_PORT         +
-                      ViewOptions::WITH_PATH_NAME    +
-                      ViewOptions::WITH_QUERY_STR    +
-                      ViewOptions::WITH_FRAGMENT     +
-                      ViewOptions::EMPTY_AUTHORITY   +
-                      ViewOptions::EMPTY_PATH_NAME;
+    ** 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 &regx)
+                   const std::string &name,
+                   const std::string &regx,
+                   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())
+              ));
+            }
+          }
         }
       }
 
@@ -106,7 +197,7 @@ namespace zypp
 
     // ---------------------------------------------------------------
     UrlBase::UrlBase()
-      : m_data( new UrlData())
+      : m_data( new UrlBaseData())
     {
       configure();
     }
@@ -114,7 +205,7 @@ namespace zypp
 
     // ---------------------------------------------------------------
     UrlBase::UrlBase(const UrlBase &url)
-      : m_data( new UrlData( *(url.m_data)))
+      : m_data( new UrlBaseData( *(url.m_data)))
     {
     }
 
@@ -125,7 +216,7 @@ namespace zypp
                      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);
@@ -140,11 +231,15 @@ namespace zypp
                   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);
     }
 
 
@@ -159,23 +254,37 @@ namespace zypp
       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("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})+$");
     }
 
 
@@ -221,7 +330,7 @@ namespace zypp
     {
       zypp::url::UrlConfig   config(m_data->config);
       zypp::url::ViewOptions vopts(m_data->vopts);
-      *m_data = UrlData();
+      *m_data = UrlBaseData();
       m_data->config = config;
       m_data->vopts  = vopts;
     }
@@ -236,26 +345,6 @@ namespace zypp
 
 
     // ---------------------------------------------------------------
-    std::string
-    UrlBase::cleanupPathName(const std::string &path)
-    {
-      size_t pos = 0;
-
-      while( pos < path.length() && path.at(pos) == '/')
-        pos++;
-
-      if( pos > 1)
-      {
-        // make sure, there is not more than
-        // _one_ leading "/" in the path name.
-        return path.substr(pos - 1);
-      }
-
-      return std::string(path);
-    }
-
-
-    // ---------------------------------------------------------------
     zypp::url::UrlSchemes
     UrlBase::getKnownSchemes() const
     {
@@ -284,8 +373,16 @@ namespace zypp
     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)
       {
         std::string    lscheme( str::toLower(scheme));
         UrlSchemes     schemes( getKnownSchemes());
@@ -308,24 +405,48 @@ namespace zypp
     bool
     UrlBase::isValid() const
     {
-      return !getScheme().empty();
+      /*
+      ** 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
     {
-      return toString(getViewOptions());
+      return asString(getViewOptions());
     }
 
 
     // ---------------------------------------------------------------
     std::string
-    UrlBase::toString(const zypp::url::ViewOptions &opts) const
+    UrlBase::asString(const zypp::url::ViewOptions &opts) const
     {
-      std::string url;
-      UrlData     tmp;
+      std::string   url;
+      UrlBaseData   tmp;
 
       if( opts.has(ViewOptions::WITH_SCHEME))
       {
@@ -371,6 +492,10 @@ namespace zypp
                 }
               }
             }
+            else if( opts.has(ViewOptions::EMPTY_AUTHORITY))
+            {
+              url += "//";
+            }
           }
           else if( opts.has(ViewOptions::EMPTY_AUTHORITY))
           {
@@ -384,10 +509,15 @@ namespace zypp
         tmp.pathname = getPathName(zypp::url::E_ENCODED);
         if( !tmp.pathname.empty())
         {
-          if( (!tmp.host.empty() || opts.has(ViewOptions::EMPTY_AUTHORITY))
-              && (tmp.pathname.at(0) != '/'))
+          if(url.find("/") != std::string::npos)
           {
-            url += "/";
+            // 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;
 
@@ -404,7 +534,8 @@ namespace zypp
             }
           }
         }
-        else if( opts.has(ViewOptions::EMPTY_PATH_NAME))
+        else if( opts.has(ViewOptions::EMPTY_PATH_NAME)
+                 && url.find("/") != std::string::npos)
         {
           url += "/";
           if( opts.has(ViewOptions::EMPTY_PATH_PARAMS))
@@ -556,7 +687,7 @@ namespace zypp
       if(eflag == zypp::url::E_DECODED)
         return zypp::url::decode(m_data->pathname);
       else
-        return m_data->pathname;
+        return cleanupPathName(m_data->pathname);
     }
 
 
@@ -573,11 +704,18 @@ namespace zypp
     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;
     }
 
@@ -586,6 +724,13 @@ namespace zypp
     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,
@@ -614,11 +759,18 @@ namespace zypp
     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;
     }
 
@@ -627,6 +779,13 @@ namespace zypp
     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,
@@ -659,10 +818,17 @@ namespace zypp
         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())
+        ));
       }
     }
 
@@ -671,51 +837,52 @@ namespace zypp
     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("");
       }
     }
@@ -733,7 +900,6 @@ namespace zypp
       {
         checkUrlData(querystr, "query string", config("rx_querystr"));
 
-        // FIXME: split & recode?
         m_data->querystr = querystr;
       }
     }
@@ -741,7 +907,8 @@ namespace zypp
 
     // ---------------------------------------------------------------
     void
-    UrlBase::setFragment(const std::string &fragment)
+    UrlBase::setFragment(const std::string &fragment,
+                         EEncoding         eflag)
     {
       if( fragment.empty())
       {
@@ -749,20 +916,26 @@ namespace zypp
       }
       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())
       {
@@ -770,20 +943,33 @@ namespace zypp
       }
       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())
       {
@@ -791,13 +977,25 @@ namespace zypp
       }
       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")
+          );
+        }
       }
     }
 
@@ -808,17 +1006,49 @@ namespace zypp
     {
       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())
+          ));
+        }
       }
     }
 
@@ -833,32 +1063,84 @@ namespace zypp
       }
       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( cleanupPathName(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")
+            )
+          );
+        }
       }
     }
 
@@ -875,7 +1157,6 @@ namespace zypp
       {
         checkUrlData(params, "path parameters", config("rx_pathparams"));
 
-        // FIXME: split & recode?
         m_data->pathparams = params;
       }
     }
@@ -898,6 +1179,13 @@ namespace zypp
     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,
@@ -913,12 +1201,8 @@ namespace zypp
     void
     UrlBase::setPathParam(const std::string &param, const std::string &value)
     {
-          std::string raw_param( zypp::url::decode(param));
-          std::string raw_value( zypp::url::decode(value));
-
           zypp::url::ParamMap pmap( getPathParamsMap(zypp::url::E_DECODED));
-          pmap[raw_param] = raw_value;
-
+          pmap[param] = value;
           setPathParamsMap(pmap);
     }
 
@@ -940,6 +1224,13 @@ namespace zypp
     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,
@@ -954,16 +1245,129 @@ namespace zypp
     void
     UrlBase::setQueryParam(const std::string &param, const std::string &value)
     {
-          std::string raw_param( zypp::url::decode(param));
-          std::string raw_value( zypp::url::decode(value));
-
           zypp::url::ParamMap pmap( getQueryStringMap(zypp::url::E_DECODED));
-          pmap[raw_param] = raw_value;
+          pmap[param] = value;
+          setQueryStringMap(pmap);
+    }
 
+    // ---------------------------------------------------------------
+    void
+    UrlBase::delQueryParam(const std::string &param)
+    {
+          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
   ////////////////////////////////////////////////////////////////////