UrlBase.cc

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00012 #include <zypp/url/UrlBase.h>
00013 #include <zypp/base/String.h>
00014 #include <zypp/base/Gettext.h>
00015 #include <zypp/base/Regex.h>
00016 
00017 #include <stdexcept>
00018 #include <climits>
00019 #include <errno.h>
00020 #include <sys/types.h>
00021 #include <sys/socket.h>
00022 #include <arpa/inet.h>
00023 
00024 #include <iostream>
00025 
00026 // in the Estonian locale, a-z excludes t, for example. #302525
00027 // http://en.wikipedia.org/wiki/Estonian_alphabet
00028 #define a_zA_Z "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
00029 
00030 // ---------------------------------------------------------------
00031 /*
00032 ** authority = //[user [:password] @ ] host [:port]
00033 **
00034 ** host      = hostname | IPv4 | "[" IPv6-IP "]" | "[v...]"
00035 */
00036 #define RX_VALID_SCHEME    "^[" a_zA_Z "][" a_zA_Z "0-9\\.+-]*$"
00037 
00038 #define RX_VALID_PORT      "^[0-9]{1,5}$"
00039 
00040 #define RX_VALID_HOSTNAME  "^[[:alnum:]]+([\\.-][[:alnum:]]+)*$"
00041 
00042 #define RX_VALID_HOSTIPV4  \
00043         "^([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$"
00044 
00045 #define RX_VALID_HOSTIPV6  \
00046         "^\\[[:a-fA-F0-9]+(:[0-9]{1,3}(\\.[0-9]{1,3}){3})?\\]$"
00047 
00048 
00050 namespace zypp
00051 { 
00052 
00054   namespace url
00055   { 
00056 
00057 
00058     // ---------------------------------------------------------------
00059     /*
00060     ** URL asString() view option constants:
00061     */
00062     const ViewOption  ViewOption::WITH_SCHEME       = 0x0001;
00063     const ViewOption  ViewOption::WITH_USERNAME     = 0x0002;
00064     const ViewOption  ViewOption::WITH_PASSWORD     = 0x0004;
00065     const ViewOption  ViewOption::WITH_HOST         = 0x0008;
00066     const ViewOption  ViewOption::WITH_PORT         = 0x0010;
00067     const ViewOption  ViewOption::WITH_PATH_NAME    = 0x0020;
00068     const ViewOption  ViewOption::WITH_PATH_PARAMS  = 0x0040;
00069     const ViewOption  ViewOption::WITH_QUERY_STR    = 0x0080;
00070     const ViewOption  ViewOption::WITH_FRAGMENT     = 0x0100;
00071     const ViewOption  ViewOption::EMPTY_AUTHORITY   = 0x0200;
00072     const ViewOption  ViewOption::EMPTY_PATH_NAME   = 0x0400;
00073     const ViewOption  ViewOption::EMPTY_PATH_PARAMS = 0x0800;
00074     const ViewOption  ViewOption::EMPTY_QUERY_STR   = 0x1000;
00075     const ViewOption  ViewOption::EMPTY_FRAGMENT    = 0x2000;
00076     const ViewOption  ViewOption::DEFAULTS          = 0x07bb;
00077     /*
00078     const ViewOption  ViewOption::DEFAULTS          =
00079                       ViewOption::WITH_SCHEME       +
00080                       ViewOption::WITH_USERNAME     +
00081                       ViewOption::WITH_HOST         +
00082                       ViewOption::WITH_PORT         +
00083                       ViewOption::WITH_PATH_NAME    +
00084                       ViewOption::WITH_QUERY_STR    +
00085                       ViewOption::WITH_FRAGMENT     +
00086                       ViewOption::EMPTY_AUTHORITY   +
00087                       ViewOption::EMPTY_PATH_NAME;
00088     */
00089 
00090     // ---------------------------------------------------------------
00091     ViewOption::ViewOption()
00092       : opt(0x07bb)
00093     {}
00094 
00095     // ---------------------------------------------------------------
00096     ViewOption::ViewOption(int option)
00097       : opt(option)
00098     {}
00099 
00100 
00101     // ---------------------------------------------------------------
00102     /*
00103     ** Behaviour configuration variables.
00104     */
00105     typedef std::map< std::string, std::string > UrlConfig;
00106 
00107 
00108     // ---------------------------------------------------------------
00112     class UrlBaseData
00113     {
00114     public:
00115       UrlBaseData()
00116       {}
00117 
00118       UrlBaseData(const UrlConfig &conf)
00119         : config(conf)
00120       {}
00121 
00122       UrlConfig       config;
00123       ViewOptions     vopts;
00124 
00125       std::string     scheme;
00126       std::string     user;
00127       std::string     pass;
00128       std::string     host;
00129       std::string     port;
00130       std::string     pathname;
00131       std::string     pathparams;
00132       std::string     querystr;
00133       std::string     fragment;
00134     };
00135 
00136 
00137     // ---------------------------------------------------------------
00138     /*
00139     ** Anonymous/internal utility namespace:
00140     */
00141     namespace // anonymous
00142     {
00143 
00144                         // -------------------------------------------------------------
00145       inline void
00146       checkUrlData(const std::string &data,
00147                    const std::string &name,
00148                    const std::string &regx,
00149                    bool               show=true)
00150       {
00151         if( regx.empty() || regx == "^$")
00152         {
00153           ZYPP_THROW(UrlNotAllowedException(
00154             str::form(_("Url scheme does not allow a %s"), name.c_str())
00155           ));
00156         }
00157         else
00158         {
00159           bool valid = false;
00160           try
00161           {
00162             str::regex rex(regx);
00163             valid = str::regex_match(data, rex);
00164           }
00165           catch( ... )
00166           {}
00167 
00168           if( !valid)
00169           {
00170             if( show)
00171             {
00172               ZYPP_THROW(UrlBadComponentException(
00173                 str::form(_("Invalid %s component '%s'"),
00174                           name.c_str(), data.c_str())
00175               ));
00176             }
00177             else
00178             {
00179               ZYPP_THROW(UrlBadComponentException(
00180                 str::form(_("Invalid %s component"), name.c_str())
00181               ));
00182             }
00183           }
00184         }
00185       }
00186 
00187     } // namespace
00188 
00189 
00190     // ---------------------------------------------------------------
00191     UrlBase::~UrlBase()
00192     {
00193       delete m_data;
00194       m_data = NULL;
00195     }
00196 
00197 
00198     // ---------------------------------------------------------------
00199     UrlBase::UrlBase()
00200       : m_data( new UrlBaseData())
00201     {
00202       configure();
00203     }
00204 
00205 
00206     // ---------------------------------------------------------------
00207     UrlBase::UrlBase(const UrlBase &url)
00208       : m_data( new UrlBaseData( *(url.m_data)))
00209     {
00210     }
00211 
00212 
00213     // ---------------------------------------------------------------
00214     UrlBase::UrlBase(const std::string &scheme,
00215                      const std::string &authority,
00216                      const std::string &pathdata,
00217                      const std::string &querystr,
00218                      const std::string &fragment)
00219       : m_data( new UrlBaseData())
00220     {
00221       configure();
00222       init(scheme, authority, pathdata, querystr, fragment);
00223     }
00224 
00225 
00226     // ---------------------------------------------------------------
00227     void
00228     UrlBase::init(const std::string &scheme,
00229                   const std::string &authority,
00230                   const std::string &pathdata,
00231                   const std::string &querystr,
00232                   const std::string &fragment)
00233     {
00234       setScheme(scheme);
00235       setAuthority(authority);
00236       setPathData(pathdata);
00237       setQueryString(querystr);
00238       setFragment(fragment, zypp::url::E_ENCODED);
00239     }
00240 
00241 
00242     // ---------------------------------------------------------------
00243     void
00244     UrlBase::configure()
00245     {
00246       config("sep_pathparams",  ";");
00247       config("psep_pathparam",  ",");
00248       config("vsep_pathparam",  "=");
00249 
00250       config("psep_querystr",   "&");
00251       config("vsep_querystr",   "=");
00252 
00253       config("safe_username",   "~!$&'()*+=,;");
00254       config("safe_password",   "~!$&'()*+=,:;");
00255       config("safe_hostname",   "[:]");
00256       config("safe_pathname",   "~!$&'()*+=,:@/");
00257       config("safe_pathparams", "~!$&'()*+=,:;@/");
00258       config("safe_querystr",   "~!$&'()*+=,:;@/?");
00259       config("safe_fragment",   "~!$&'()*+=,:;@/?");
00260 
00261       // y=yes (allowed)
00262       // n=no  (disallowed, exception if !empty)
00263       config("with_authority",  "y");
00264       config("with_port",       "y");
00265 
00266       // y=yes (required but don't throw if empty)
00267       // n=no  (not required, ignore if empty)
00268       // m=mandatory (exception if empty)
00269       config("require_host",    "n");
00270       config("require_pathname","n");
00271 
00272       // y=yes (encode 2. slash even if authority present)
00273       // n=no  (don't encode 2. slash if authority present)
00274       config("path_encode_slash2", "n");
00275 
00276       config("rx_username",     "^([" a_zA_Z "0-9!$&'\\(\\)*+=,;~\\._-]|%[a-fA-F0-9]{2})+$");
00277       config("rx_password",     "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:;~\\._-]|%[a-fA-F0-9]{2})+$");
00278 
00279       config("rx_pathname",     "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:@/~\\._-]|%[a-fA-F0-9]{2})+$");
00280       config("rx_pathparams",   "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:;@/~\\._-]|%[a-fA-F0-9]{2})+$");
00281 
00282       config("rx_querystr",     "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:;@/?~\\._-]|%[a-fA-F0-9]{2})+$");
00283       config("rx_fragment",     "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:;@/?~\\._-]|%[a-fA-F0-9]{2})+$");
00284     }
00285 
00286 
00287     // ---------------------------------------------------------------
00288     void
00289     UrlBase::config(const std::string &opt, const std::string &val)
00290     {
00291       m_data->config[opt] = val;
00292     }
00293 
00294 
00295     // ---------------------------------------------------------------
00296     std::string
00297     UrlBase::config(const std::string &opt) const
00298     {
00299       UrlConfig::const_iterator v( m_data->config.find(opt));
00300       if( v != m_data->config.end())
00301         return v->second;
00302       else
00303         return std::string();
00304     }
00305 
00306 
00307     // ---------------------------------------------------------------
00308     ViewOptions
00309     UrlBase::getViewOptions() const
00310     {
00311       return m_data->vopts;
00312     }
00313 
00314 
00315     // ---------------------------------------------------------------
00316     void
00317     UrlBase::setViewOptions(const ViewOptions &vopts)
00318     {
00319         m_data->vopts = vopts;
00320     }
00321 
00322 
00323     // ---------------------------------------------------------------
00324     void
00325     UrlBase::clear()
00326     {
00327       zypp::url::UrlConfig   config(m_data->config);
00328       zypp::url::ViewOptions vopts(m_data->vopts);
00329       *m_data = UrlBaseData();
00330       m_data->config = config;
00331       m_data->vopts  = vopts;
00332     }
00333 
00334 
00335     // ---------------------------------------------------------------
00336     UrlBase *
00337     UrlBase::clone() const
00338     {
00339       return new UrlBase(*this);
00340     }
00341 
00342 
00343     // ---------------------------------------------------------------
00344     zypp::url::UrlSchemes
00345     UrlBase::getKnownSchemes() const
00346     {
00347       return UrlSchemes();
00348     }
00349 
00350 
00351     // ---------------------------------------------------------------
00352     bool
00353     UrlBase::isKnownScheme(const std::string &scheme) const
00354     {
00355       std::string                lscheme( str::toLower(scheme));
00356       UrlSchemes                 schemes( getKnownSchemes());
00357       UrlSchemes::const_iterator s;
00358 
00359       for(s=schemes.begin(); s!=schemes.end(); ++s)
00360       {
00361         if( lscheme == str::toLower(*s))
00362           return true;
00363       }
00364       return false;
00365     }
00366 
00367 
00368     // ---------------------------------------------------------------
00369     bool
00370     UrlBase::isValidScheme(const std::string &scheme) const
00371     {
00372       bool valid = false;
00373       try
00374       {
00375         str::regex rex(RX_VALID_SCHEME);
00376         valid = str::regex_match(scheme, rex);
00377       }
00378       catch( ... )
00379       {}
00380 
00381       if(valid)
00382       {
00383         std::string    lscheme( str::toLower(scheme));
00384         UrlSchemes     schemes( getKnownSchemes());
00385 
00386         if( schemes.empty())
00387           return true;
00388 
00389         UrlSchemes::const_iterator s;
00390         for(s=schemes.begin(); s!=schemes.end(); ++s)
00391         {
00392           if( lscheme == str::toLower(*s))
00393             return true;
00394         }
00395       }
00396       return false;
00397     }
00398 
00399 
00400     // ---------------------------------------------------------------
00401     bool
00402     UrlBase::isValid() const
00403     {
00404       /*
00405       ** scheme is the only mandatory component
00406       ** for all url's and is already verified,
00407       ** (except for empty Url instances), so
00408       ** Url with empty scheme is never valid.
00409       */
00410       if( getScheme().empty())
00411         return false;
00412 
00413       std::string host( getHost(zypp::url::E_ENCODED));
00414       if( host.empty() && config("require_host")     != "n")
00415         return false;
00416 
00417       std::string path( getPathName(zypp::url::E_ENCODED));
00418       if( path.empty() && config("require_pathname") != "n")
00419         return false;
00420 
00421       /*
00422       ** path has to begin with "/" if authority avaliable
00423       ** if host is set after the pathname, we can't throw
00424       */
00425       if( !host.empty() && !path.empty() && path.at(0) != '/')
00426         return false;
00427 
00428       return true;
00429     }
00430 
00431 
00432     // ---------------------------------------------------------------
00433     std::string
00434     UrlBase::asString() const
00435     {
00436       return asString(getViewOptions());
00437     }
00438 
00439 
00440     // ---------------------------------------------------------------
00441     std::string
00442     UrlBase::asString(const zypp::url::ViewOptions &opts) const
00443     {
00444       std::string   url;
00445       UrlBaseData   tmp;
00446 
00447       if( opts.has(ViewOptions::WITH_SCHEME))
00448       {
00449         tmp.scheme = getScheme();
00450         if( !tmp.scheme.empty())
00451         {
00452           url += tmp.scheme + ":";
00453 
00454           if( opts.has(ViewOptions::WITH_HOST))
00455           {
00456             tmp.host = getHost(zypp::url::E_ENCODED);
00457             if( !tmp.host.empty())
00458             {
00459               url += "//";
00460 
00461               if( opts.has(ViewOptions::WITH_USERNAME))
00462               {
00463                 tmp.user = getUsername(zypp::url::E_ENCODED);
00464                 if( !tmp.user.empty())
00465                 {
00466                   url += tmp.user;
00467 
00468                   if( opts.has(ViewOptions::WITH_PASSWORD))
00469                   {
00470                     tmp.pass = getPassword(zypp::url::E_ENCODED);
00471                     if( !tmp.pass.empty())
00472                     {
00473                       url += ":" + tmp.pass;
00474                     }
00475                   }
00476                   url += "@";
00477                 }
00478               }
00479 
00480               url += tmp.host;
00481 
00482               if( opts.has(ViewOptions::WITH_PORT))
00483               {
00484                 tmp.port = getPort();
00485                 if( !tmp.port.empty())
00486                 {
00487                   url += ":" + tmp.port;
00488                 }
00489               }
00490             }
00491             else if( opts.has(ViewOptions::EMPTY_AUTHORITY))
00492             {
00493               url += "//";
00494             }
00495           }
00496           else if( opts.has(ViewOptions::EMPTY_AUTHORITY))
00497           {
00498             url += "//";
00499           }
00500         }
00501       }
00502 
00503       if( opts.has(ViewOptions::WITH_PATH_NAME))
00504       {
00505         tmp.pathname = getPathName(zypp::url::E_ENCODED);
00506         if( !tmp.pathname.empty())
00507         {
00508           if(url.find("/") != std::string::npos)
00509           {
00510             // Url contains authority (that may be empty),
00511             // we may need a rewrite of the encoded path.
00512             tmp.pathname = cleanupPathName(tmp.pathname, true);
00513             if(tmp.pathname.at(0) != '/')
00514             {
00515               url += "/";
00516             }
00517           }
00518           url += tmp.pathname;
00519 
00520           if( opts.has(ViewOptions::WITH_PATH_PARAMS))
00521           {
00522             tmp.pathparams = getPathParams();
00523             if( !tmp.pathparams.empty())
00524             {
00525               url += ";" + tmp.pathparams;
00526             }
00527             else if( opts.has(ViewOptions::EMPTY_PATH_PARAMS))
00528             {
00529               url += ";";
00530             }
00531           }
00532         }
00533         else if( opts.has(ViewOptions::EMPTY_PATH_NAME)
00534                  && url.find("/") != std::string::npos)
00535         {
00536           url += "/";
00537           if( opts.has(ViewOptions::EMPTY_PATH_PARAMS))
00538           {
00539             url += ";";
00540           }
00541         }
00542       }
00543 
00544       if( opts.has(ViewOptions::WITH_QUERY_STR))
00545       {
00546         tmp.querystr = getQueryString();
00547         if( !tmp.querystr.empty())
00548         {
00549           url += "?" + tmp.querystr;
00550         }
00551         else if( opts.has(ViewOptions::EMPTY_QUERY_STR))
00552         {
00553           url += "?";
00554         }
00555       }
00556 
00557       if( opts.has(ViewOptions::WITH_FRAGMENT))
00558       {
00559         tmp.fragment = getFragment(zypp::url::E_ENCODED);
00560         if( !tmp.fragment.empty())
00561         {
00562           url += "#" + tmp.fragment;
00563         }
00564         else if( opts.has(ViewOptions::EMPTY_FRAGMENT))
00565         {
00566           url += "#";
00567         }
00568       }
00569 
00570       return url;
00571     }
00572 
00573 
00574     // ---------------------------------------------------------------
00575     std::string
00576     UrlBase::getScheme() const
00577     {
00578       return m_data->scheme;
00579     }
00580 
00581 
00582     // ---------------------------------------------------------------
00583     std::string
00584     UrlBase::getAuthority() const
00585     {
00586       std::string str;
00587       if( !getHost(zypp::url::E_ENCODED).empty())
00588       {
00589         if( !getUsername(zypp::url::E_ENCODED).empty())
00590         {
00591           str = getUsername(zypp::url::E_ENCODED);
00592           if( !getPassword(zypp::url::E_ENCODED).empty())
00593           {
00594             str += ":" + getPassword(zypp::url::E_ENCODED);
00595           }
00596           str += "@";
00597         }
00598 
00599         str += getHost(zypp::url::E_ENCODED);
00600         if( !getPort().empty())
00601         {
00602           str += ":" + getPort();
00603         }
00604       }
00605       return str;
00606     }
00607 
00608 
00609     // ---------------------------------------------------------------
00610     std::string
00611     UrlBase::getPathData() const
00612     {
00613       return getPathName(zypp::url::E_ENCODED) +
00614              config("sep_pathparams") +
00615              getPathParams();
00616     }
00617 
00618 
00619     // ---------------------------------------------------------------
00620     std::string
00621     UrlBase::getQueryString() const
00622     {
00623       return m_data->querystr;
00624     }
00625 
00626 
00627     // ---------------------------------------------------------------
00628     std::string
00629     UrlBase::getFragment(EEncoding eflag) const
00630     {
00631       if(eflag == zypp::url::E_DECODED)
00632         return zypp::url::decode(m_data->fragment);
00633       else
00634         return m_data->fragment;
00635     }
00636 
00637 
00638     // ---------------------------------------------------------------
00639     std::string
00640     UrlBase::getUsername(EEncoding eflag) const
00641     {
00642       if(eflag == zypp::url::E_DECODED)
00643         return zypp::url::decode(m_data->user);
00644       else
00645         return m_data->user;
00646     }
00647 
00648 
00649     // ---------------------------------------------------------------
00650     std::string
00651     UrlBase::getPassword(EEncoding eflag) const
00652     {
00653       if(eflag == zypp::url::E_DECODED)
00654         return zypp::url::decode(m_data->pass);
00655       else
00656         return m_data->pass;
00657     }
00658 
00659 
00660     // ---------------------------------------------------------------
00661     std::string
00662     UrlBase::getHost(EEncoding eflag) const
00663     {
00664       if(eflag == zypp::url::E_DECODED)
00665         return zypp::url::decode(m_data->host);
00666       else
00667         return m_data->host;
00668     }
00669 
00670 
00671     // ---------------------------------------------------------------
00672     std::string
00673     UrlBase::getPort() const
00674     {
00675       return m_data->port;
00676     }
00677 
00678 
00679     // ---------------------------------------------------------------
00680     std::string
00681     UrlBase::getPathName(EEncoding eflag) const
00682     {
00683       if(eflag == zypp::url::E_DECODED)
00684         return zypp::url::decode(m_data->pathname);
00685       else
00686         return cleanupPathName(m_data->pathname);
00687     }
00688 
00689 
00690     // ---------------------------------------------------------------
00691     std::string
00692     UrlBase::getPathParams() const
00693     {
00694       return m_data->pathparams;
00695     }
00696 
00697 
00698     // ---------------------------------------------------------------
00699     zypp::url::ParamVec
00700     UrlBase::getPathParamsVec() const
00701     {
00702       zypp::url::ParamVec pvec;
00703       if( config("psep_pathparam").empty())
00704       {
00705         pvec.push_back(getPathParams());
00706       }
00707       else
00708       {
00709         zypp::url::split(
00710           pvec,
00711           getPathParams(),
00712           config("psep_pathparam")
00713         );
00714       }
00715       return pvec;
00716     }
00717 
00718 
00719     // ---------------------------------------------------------------
00720     zypp::url::ParamMap
00721     UrlBase::getPathParamsMap(EEncoding eflag) const
00722     {
00723       if( config("psep_pathparam").empty() ||
00724           config("vsep_pathparam").empty())
00725       {
00726         ZYPP_THROW(UrlNotSupportedException(
00727           "Path parameter parsing not supported for this URL"
00728         ));
00729       }
00730       zypp::url::ParamMap pmap;
00731       zypp::url::split(
00732         pmap,
00733         getPathParams(),
00734         config("psep_pathparam"),
00735         config("vsep_pathparam"),
00736         eflag
00737       );
00738       return pmap;
00739     }
00740 
00741 
00742     // ---------------------------------------------------------------
00743     std::string
00744     UrlBase::getPathParam(const std::string &param, EEncoding eflag) const
00745     {
00746       zypp::url::ParamMap pmap( getPathParamsMap( eflag));
00747       zypp::url::ParamMap::const_iterator i( pmap.find(param));
00748 
00749       return i != pmap.end() ? i->second : std::string();
00750     }
00751 
00752 
00753     // ---------------------------------------------------------------
00754     zypp::url::ParamVec
00755     UrlBase::getQueryStringVec() const
00756     {
00757       zypp::url::ParamVec pvec;
00758       if( config("psep_querystr").empty())
00759       {
00760         pvec.push_back(getQueryString());
00761       }
00762       else
00763       {
00764         zypp::url::split(
00765           pvec,
00766           getQueryString(),
00767           config("psep_querystr")
00768         );
00769       }
00770       return pvec;
00771     }
00772 
00773 
00774     // ---------------------------------------------------------------
00775     zypp::url::ParamMap
00776     UrlBase::getQueryStringMap(EEncoding eflag) const
00777     {
00778       if( config("psep_querystr").empty() ||
00779           config("vsep_querystr").empty())
00780       {
00781         ZYPP_THROW(UrlNotSupportedException(
00782           _("Query string parsing not supported for this URL")
00783         ));
00784       }
00785       zypp::url::ParamMap pmap;
00786       zypp::url::split(
00787         pmap,
00788         getQueryString(),
00789         config("psep_querystr"),
00790         config("vsep_querystr"),
00791         eflag
00792       );
00793       return pmap;
00794     }
00795 
00796 
00797     // ---------------------------------------------------------------
00798     std::string
00799     UrlBase::getQueryParam(const std::string &param, EEncoding eflag) const
00800     {
00801       zypp::url::ParamMap pmap( getQueryStringMap( eflag));
00802       zypp::url::ParamMap::const_iterator i( pmap.find(param));
00803 
00804       return i != pmap.end() ? i->second : std::string();
00805     }
00806 
00807 
00808     // ---------------------------------------------------------------
00809     void
00810     UrlBase::setScheme(const std::string &scheme)
00811     {
00812       if( isValidScheme(scheme))
00813       {
00814         m_data->scheme = str::toLower(scheme);
00815       }
00816       else
00817       if( scheme.empty())
00818       {
00819         ZYPP_THROW(UrlBadComponentException(
00820           _("Url scheme is a required component")
00821         ));
00822       }
00823       else
00824       {
00825         ZYPP_THROW(UrlBadComponentException(
00826           str::form(_("Invalid Url scheme '%s'"), scheme.c_str())
00827         ));
00828       }
00829     }
00830 
00831 
00832     // ---------------------------------------------------------------
00833     void
00834     UrlBase::setAuthority(const std::string &authority)
00835     {
00836       std::string s = authority;
00837       std::string::size_type p,q;
00838 
00839       std::string username, password, host, port;
00840 
00841       if ((p=s.find('@')) != std::string::npos)
00842       {
00843         q = s.find(':');
00844         if (q != std::string::npos && q < p)
00845         {
00846           setUsername(s.substr(0, q), zypp::url::E_ENCODED);
00847           setPassword(s.substr(q+1, p-q-1), zypp::url::E_ENCODED);
00848         }
00849         else
00850           setUsername(s.substr(0, p), zypp::url::E_ENCODED);
00851         s = s.substr(p+1);
00852       }
00853       if ((p = s.rfind(':')) != std::string::npos && ( (q = s.rfind(']')) == std::string::npos || q < p) )
00854       {
00855         setHost(s.substr(0, p));
00856         setPort(s.substr(p+1));
00857       }
00858       else
00859         setHost(s);
00860     }
00861 
00862     // ---------------------------------------------------------------
00863     void
00864     UrlBase::setPathData(const std::string &pathdata)
00865     {
00866       size_t      pos = std::string::npos;
00867       std::string sep(config("sep_pathparams"));
00868 
00869       if( !sep.empty())
00870         pos = pathdata.find(sep);
00871 
00872       if( pos != std::string::npos)
00873       {
00874         setPathName(pathdata.substr(0, pos),
00875                     zypp::url::E_ENCODED);
00876         setPathParams(pathdata.substr(pos + 1));
00877       }
00878       else
00879       {
00880         setPathName(pathdata,
00881                     zypp::url::E_ENCODED);
00882         setPathParams("");
00883       }
00884     }
00885 
00886 
00887     // ---------------------------------------------------------------
00888     void
00889     UrlBase::setQueryString(const std::string &querystr)
00890     {
00891       if( querystr.empty())
00892       {
00893         m_data->querystr = querystr;
00894       }
00895       else
00896       {
00897         checkUrlData(querystr, "query string", config("rx_querystr"));
00898 
00899         m_data->querystr = querystr;
00900       }
00901     }
00902 
00903 
00904     // ---------------------------------------------------------------
00905     void
00906     UrlBase::setFragment(const std::string &fragment,
00907                          EEncoding         eflag)
00908     {
00909       if( fragment.empty())
00910       {
00911         m_data->fragment = fragment;
00912       }
00913       else
00914       {
00915         if(eflag == zypp::url::E_ENCODED)
00916         {
00917           checkUrlData(fragment, "fragment", config("rx_fragment"));
00918 
00919           m_data->fragment = fragment;
00920         }
00921         else
00922         {
00923           m_data->fragment = zypp::url::encode(
00924             fragment, config("safe_fragment")
00925           );
00926         }
00927       }
00928     }
00929 
00930 
00931     // ---------------------------------------------------------------
00932     void
00933     UrlBase::setUsername(const std::string &user,
00934                          EEncoding         eflag)
00935     {
00936       if( user.empty())
00937       {
00938         m_data->user = user;
00939       }
00940       else
00941       {
00942         if( config("with_authority") != "y")
00943         {
00944           ZYPP_THROW(UrlNotAllowedException(
00945             _("Url scheme does not allow a username")
00946           ));
00947         }
00948 
00949         if(eflag == zypp::url::E_ENCODED)
00950         {
00951           checkUrlData(user, "username", config("rx_username"));
00952 
00953           m_data->user = user;
00954         }
00955         else
00956         {
00957           m_data->user = zypp::url::encode(
00958             user, config("safe_username")
00959           );
00960         }
00961       }
00962     }
00963 
00964 
00965     // ---------------------------------------------------------------
00966     void
00967     UrlBase::setPassword(const std::string &pass,
00968                          EEncoding         eflag)
00969     {
00970       if( pass.empty())
00971       {
00972         m_data->pass = pass;
00973       }
00974       else
00975       {
00976         if( config("with_authority") != "y")
00977         {
00978           ZYPP_THROW(UrlNotAllowedException(
00979             _("Url scheme does not allow a password")
00980           ));
00981         }
00982 
00983         if(eflag == zypp::url::E_ENCODED)
00984         {
00985           checkUrlData(pass, "password", config("rx_password"), false);
00986 
00987           m_data->pass = pass;
00988         }
00989         else
00990         {
00991           m_data->pass = zypp::url::encode(
00992             pass, config("safe_password")
00993           );
00994         }
00995       }
00996     }
00997 
00998 
00999     // ---------------------------------------------------------------
01000     void
01001     UrlBase::setHost(const std::string &host)
01002     {
01003       if( host.empty())
01004       {
01005         if(config("require_host") == "m")
01006         {
01007           ZYPP_THROW(UrlNotAllowedException(
01008             _("Url scheme requires a host component")
01009           ));
01010         }
01011         m_data->host = host;
01012       }
01013       else
01014       {
01015         if( config("with_authority") != "y")
01016         {
01017           ZYPP_THROW(UrlNotAllowedException(
01018             _("Url scheme does not allow a host component")
01019           ));
01020         }
01021 
01022         if( isValidHost(host))
01023         {
01024           std::string temp;
01025 
01026           // always decode in case isValidHost()
01027           // is reimplemented and supports also
01028           // the [v ... ] notation.
01029           if( host.at(0) == '[')
01030           {
01031             temp = str::toUpper(zypp::url::decode(host));
01032           }
01033           else
01034           {
01035             temp = str::toLower(zypp::url::decode(host));
01036           }
01037 
01038           m_data->host = zypp::url::encode(
01039             temp, config("safe_hostname")
01040           );
01041         }
01042         else
01043         {
01044           ZYPP_THROW(UrlBadComponentException(
01045             str::form(_("Invalid host component '%s'"), host.c_str())
01046           ));
01047         }
01048       }
01049     }
01050 
01051 
01052     // ---------------------------------------------------------------
01053     void
01054     UrlBase::setPort(const std::string &port)
01055     {
01056       if( port.empty())
01057       {
01058         m_data->port = port;
01059       }
01060       else
01061       {
01062         if( config("with_authority") != "y" ||
01063             config("with_port")      != "y")
01064         {
01065           ZYPP_THROW(UrlNotAllowedException(
01066             _("Url scheme does not allow a port")
01067           ));
01068         }
01069 
01070         if( isValidPort(port))
01071         {
01072           m_data->port = port;
01073         }
01074         else
01075         {
01076           ZYPP_THROW(UrlBadComponentException(
01077             str::form(_("Invalid port component '%s'"), port.c_str())
01078           ));
01079         }
01080       }
01081     }
01082 
01083 
01084     // ---------------------------------------------------------------
01085     void
01086     UrlBase::setPathName(const std::string &path,
01087                          EEncoding         eflag)
01088     {
01089       if( path.empty())
01090       {
01091         if(config("require_pathname") == "m")
01092         {
01093           ZYPP_THROW(UrlNotAllowedException(
01094             _("Url scheme requires path name")
01095           ));
01096         }
01097         m_data->pathname = path;
01098       }
01099       else
01100       {
01101         if(eflag == zypp::url::E_ENCODED)
01102         {
01103           checkUrlData(path, "path name", config("rx_pathname"));
01104 
01105           if( !getHost(zypp::url::E_ENCODED).empty())
01106           {
01107             // has to begin with a "/". For consistency with
01108             // setPathName while the host is empty, we allow
01109             // it in encoded ("%2f") form - cleanupPathName()
01110             // will fix / decode the first slash if needed.
01111             if(!(path.at(0) == '/' || (path.size() >= 3 &&
01112                  str::toLower(path.substr(0, 3)) == "%2f")))
01113             {
01114               ZYPP_THROW(UrlNotAllowedException(
01115                 _("Relative path not allowed if authority exists")
01116               ));
01117             }
01118           }
01119 
01120           m_data->pathname = cleanupPathName(path);
01121         }
01122         else //     zypp::url::E_DECODED
01123         {
01124           if( !getHost(zypp::url::E_ENCODED).empty())
01125           {
01126             if(path.at(0) != '/')
01127             {
01128               ZYPP_THROW(UrlNotAllowedException(
01129                 _("Relative path not allowed if authority exists")
01130               ));
01131             }
01132           }
01133 
01134           m_data->pathname = cleanupPathName(
01135             zypp::url::encode(
01136               path, config("safe_pathname")
01137             )
01138           );
01139         }
01140       }
01141     }
01142 
01143 
01144     // ---------------------------------------------------------------
01145     void
01146     UrlBase::setPathParams(const std::string &params)
01147     {
01148       if( params.empty())
01149       {
01150         m_data->pathparams = params;
01151       }
01152       else
01153       {
01154         checkUrlData(params, "path parameters", config("rx_pathparams"));
01155 
01156         m_data->pathparams = params;
01157       }
01158     }
01159 
01160 
01161     // ---------------------------------------------------------------
01162     void
01163     UrlBase::setPathParamsVec(const zypp::url::ParamVec &pvec)
01164     {
01165       setPathParams(
01166         zypp::url::join(
01167           pvec,
01168           config("psep_pathparam")
01169         )
01170       );
01171     }
01172 
01173 
01174     // ---------------------------------------------------------------
01175     void
01176     UrlBase::setPathParamsMap(const zypp::url::ParamMap &pmap)
01177     {
01178       if( config("psep_pathparam").empty() ||
01179           config("vsep_pathparam").empty())
01180       {
01181         ZYPP_THROW(UrlNotSupportedException(
01182           "Path Parameter parsing not supported for this URL"
01183         ));
01184       }
01185       setPathParams(
01186         zypp::url::join(
01187           pmap,
01188           config("psep_pathparam"),
01189           config("vsep_pathparam"),
01190           config("safe_pathparams")
01191         )
01192       );
01193     }
01194 
01195 
01196     // ---------------------------------------------------------------
01197     void
01198     UrlBase::setPathParam(const std::string &param, const std::string &value)
01199     {
01200           zypp::url::ParamMap pmap( getPathParamsMap(zypp::url::E_DECODED));
01201           pmap[param] = value;
01202           setPathParamsMap(pmap);
01203     }
01204 
01205 
01206     // ---------------------------------------------------------------
01207     void
01208     UrlBase::setQueryStringVec(const zypp::url::ParamVec &pvec)
01209     {
01210       setQueryString(
01211         zypp::url::join(
01212           pvec,
01213           config("psep_querystr")
01214         )
01215       );
01216     }
01217 
01218 
01219     // ---------------------------------------------------------------
01220     void
01221     UrlBase::setQueryStringMap(const zypp::url::ParamMap &pmap)
01222     {
01223       if( config("psep_querystr").empty() ||
01224           config("vsep_querystr").empty())
01225       {
01226         ZYPP_THROW(UrlNotSupportedException(
01227           _("Query string parsing not supported for this URL")
01228         ));
01229       }
01230       setQueryString(
01231         zypp::url::join(
01232           pmap,
01233           config("psep_querystr"),
01234           config("vsep_querystr"),
01235           config("safe_querystr")
01236         )
01237       );
01238     }
01239 
01240     // ---------------------------------------------------------------
01241     void
01242     UrlBase::setQueryParam(const std::string &param, const std::string &value)
01243     {
01244           zypp::url::ParamMap pmap( getQueryStringMap(zypp::url::E_DECODED));
01245           pmap[param] = value;
01246           setQueryStringMap(pmap);
01247     }
01248 
01249 
01250     // ---------------------------------------------------------------
01251     std::string
01252     UrlBase::cleanupPathName(const std::string &path) const
01253     {
01254       bool authority = !getHost(zypp::url::E_ENCODED).empty();
01255       return cleanupPathName(path, authority);
01256     }
01257 
01258     // ---------------------------------------------------------------
01259     std::string
01260     UrlBase::cleanupPathName(const std::string &path, bool authority) const
01261     {
01262       std::string copy( path);
01263 
01264       // decode the first slash if it is encoded ...
01265       if(copy.size() >= 3 && copy.at(0) != '/' &&
01266          str::toLower(copy.substr(0, 3)) == "%2f")
01267       {
01268         copy.replace(0, 3, "/");
01269       }
01270 
01271       // if path begins with a double slash ("//"); encode the second
01272       // slash [minimal and IMO sufficient] before the first path
01273       // segment, to fulfill the path-absolute rule of RFC 3986
01274       // disallowing a "//" if no authority is present.
01275       if( authority)
01276       {
01277         //
01278         // rewrite of "//" to "/%2f" not required, use config
01279         //
01280         if(config("path_encode_slash2") == "y")
01281         {
01282           // rewrite "//" ==> "/%2f"
01283           if(copy.size() >= 2 && copy.at(0) == '/' && copy.at(1) == '/')
01284           {
01285             copy.replace(1, 1, "%2F");
01286           }
01287         }
01288         else
01289         {
01290           // rewrite "/%2f" ==> "//"
01291           if(copy.size() >= 4 && copy.at(0) == '/' &&
01292              str::toLower(copy.substr(1, 4)) == "%2f")
01293           {
01294             copy.replace(1, 4, "/");
01295           }
01296         }
01297       }
01298       else
01299       {
01300         // rewrite of "//" to "/%2f" is required (no authority)
01301         if(copy.size() >= 2 && copy.at(0) == '/' && copy.at(1) == '/')
01302         {
01303           copy.replace(1, 1, "%2F");
01304         }
01305       }
01306       return copy;
01307     }
01308 
01309 
01310     // ---------------------------------------------------------------
01311     bool
01312     UrlBase::isValidHost(const std::string &host) const
01313     {
01314       try
01315       {
01316         str::regex regx(RX_VALID_HOSTIPV6);
01317         if( str::regex_match(host, regx))
01318         {
01319           struct in6_addr ip;
01320           std::string temp( host.substr(1, host.size()-2));
01321 
01322           return inet_pton(AF_INET6, temp.c_str(), &ip) > 0;
01323         }
01324         else
01325         {
01326           // matches also IPv4 dotted-decimal adresses...
01327           std::string temp( zypp::url::decode(host));
01328           str::regex  regx(RX_VALID_HOSTNAME);
01329           return str::regex_match(temp, regx);
01330         }
01331       }
01332       catch( ... )
01333       {}
01334 
01335       return false;
01336     }
01337 
01338 
01339     // ---------------------------------------------------------------
01340     bool
01341     UrlBase::isValidPort(const std::string &port) const
01342     {
01343       try
01344       {
01345         str::regex regx(RX_VALID_PORT);
01346         if( str::regex_match(port, regx))
01347         {
01348           long pnum = str::strtonum<long>(port);
01349           return ( pnum >= 1 && pnum <= USHRT_MAX);
01350         }
01351       }
01352       catch( ... )
01353       {}
01354       return false;
01355     }
01356 
01357 
01359   } // namespace url
01361 
01363 } // namespace zypp
01365 /*
01366 ** vim: set ts=2 sts=2 sw=2 ai et:
01367 */

doxygen