Url.cc

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00013 #include <zypp/Url.h>
00014 #include <zypp/base/Gettext.h>
00015 #include <zypp/base/String.h>
00016 #include <zypp/base/Regex.h>
00017 #include <stdexcept>
00018 #include <iostream>
00019 
00020 
00022 namespace zypp
00023 { 
00024 
00025 
00026   using namespace zypp::url;
00027 
00028 
00029   // -----------------------------------------------------------------
00030   /*
00031    * url       = [scheme:] [//authority] /path [?query] [#fragment]
00032    */
00033   #define RX_SPLIT_URL                       "^([^:/?#]+:|)" \
00034                                              "(//[^/?#]*|)"  \
00035                                              "([^?#]*)"        \
00036                                              "([?][^#]*|)"   \
00037                                              "(#.*|)"
00038 
00039 
00041   namespace
00042   { 
00043 
00044 
00045     // ---------------------------------------------------------------
00046     class LDAPUrl: public UrlBase
00047     {
00048     public:
00049       LDAPUrl(): UrlBase()
00050       {
00051         configure();
00052       }
00053 
00054       LDAPUrl(const LDAPUrl &url): UrlBase(url)
00055       {}
00056 
00057       virtual UrlBase *
00058       clone() const
00059       {
00060         return new LDAPUrl(*this);
00061       }
00062 
00063       virtual UrlSchemes
00064       getKnownSchemes() const
00065       {
00066         UrlSchemes schemes(2);
00067         schemes[0] = "ldap";
00068         schemes[1] = "ldaps";
00069         return schemes;
00070       }
00071 
00072       virtual void
00073       configure()
00074       {
00075         config("sep_pathparams",  "");
00076 
00077         config("psep_querystr",   "?");
00078         config("vsep_querystr",   "");
00079 
00080         // host is required (isValid=>false)
00081         // but not mandatory (see RFC 2255),
00082         // that is, accept empty host.
00083         config("require_host",    "y");
00084 
00085         // not allowed here
00086         config("rx_username",     "");
00087         config("rx_password",     "");
00088         config("rx_fragment",     "");
00089         config("rx_pathparams",   "");
00090       }
00091 
00092       virtual zypp::url::ParamMap
00093       getQueryStringMap(zypp::url::EEncoding eflag) const
00094       {
00095         static const char * const keys[] = {
00096           "attrs", "scope", "filter", "exts", NULL
00097         };
00098         zypp::url::ParamMap pmap;
00099         zypp::url::ParamVec pvec( getQueryStringVec());
00100         if( pvec.size() <= 4)
00101         {
00102           for(size_t i=0; i<pvec.size(); i++)
00103           {
00104             if(eflag == zypp::url::E_ENCODED)
00105               pmap[keys[i]] = pvec[i];
00106             else
00107               pmap[keys[i]] = zypp::url::decode( pvec[i]);
00108           }
00109         }
00110         else
00111         {
00112           ZYPP_THROW(url::UrlNotSupportedException(
00113             _("Invalid LDAP URL query string")
00114           ));
00115         }
00116         return pmap;
00117       }
00118 
00119       virtual void
00120       setQueryStringMap(const zypp::url::ParamMap &pmap)
00121       {
00122         static const char * const keys[] = {
00123           "attrs", "scope", "filter", "exts", NULL
00124         };
00125 
00126         // remove psep ("?") from safe chars
00127         std::string join_safe;
00128         std::string safe(config("safe_querystr"));
00129         std::string psep(config("psep_querystr"));
00130         for(std::string::size_type i=0; i<safe.size(); i++)
00131         {
00132           if( psep.find(safe[i]) == std::string::npos)
00133             join_safe.append(1, safe[i]);
00134         }
00135 
00136         zypp::url::ParamVec pvec(4);
00137         zypp::url::ParamMap::const_iterator p;
00138         for(p=pmap.begin(); p!=pmap.end(); ++p)
00139         {
00140           bool found=false;
00141           for(size_t i=0; i<4; i++)
00142           {
00143             if(p->first == keys[i])
00144             {
00145               found=true;
00146               pvec[i] = zypp::url::encode(p->second, join_safe);
00147             }
00148           }
00149           if( !found)
00150           {
00151             ZYPP_THROW(url::UrlNotSupportedException(
00152               str::form(_("Invalid LDAP URL query parameter '%s'"),
00153                           p->first.c_str())
00154             ));
00155           }
00156         }
00157         setQueryStringVec(pvec);
00158       }
00159     };
00160 
00161 
00162     // ---------------------------------------------------------------
00163     // FIXME: hmm..
00164     class UrlByScheme
00165     {
00166     private:
00167       typedef std::map<std::string,UrlRef> UrlBySchemeMap;
00168       UrlBySchemeMap urlByScheme;
00169 
00170     public:
00171       UrlByScheme()
00172       {
00173         UrlRef ref;
00174 
00175         // =====================================
00176         ref.reset( new LDAPUrl());
00177         addUrlByScheme("ldap", ref);
00178         addUrlByScheme("ldaps", ref);
00179 
00180 
00181         // =====================================
00182         ref.reset( new UrlBase());
00183         ref->config("with_authority",   "n");   // disallow host,...
00184         ref->config("require_pathname", "m");   // path is mandatory
00185         addUrlByScheme("hd",     ref);
00186         addUrlByScheme("cd",     ref);
00187         addUrlByScheme("dvd",    ref);
00188         addUrlByScheme("dir",    ref);
00189         addUrlByScheme("iso",    ref);
00190 
00191         // don't show empty authority
00192         ref->setViewOptions( zypp::url::ViewOption::DEFAULTS -
00193                              zypp::url::ViewOption::EMPTY_AUTHORITY);
00194         addUrlByScheme("mailto", ref);
00195         addUrlByScheme("urn",    ref);
00196         addUrlByScheme("plugin", ref);  // zypp plugable media handler:
00197 
00198         // RFC1738, 3.10: may contain a host
00199         ref->config("with_authority",   "y");   // allow host,
00200         ref->config("with_port",        "n");   // but no port,
00201         ref->config("rx_username",      "");    // username or
00202         ref->config("rx_password",      "");    // password ...
00203         addUrlByScheme("file",   ref);
00204 
00205         // =====================================
00206         ref.reset( new UrlBase());
00207         ref->config("require_host",     "m");   // host is mandatory
00208         addUrlByScheme("nfs",    ref);
00209         addUrlByScheme("nfs4",   ref);
00210         addUrlByScheme("smb",    ref);
00211         addUrlByScheme("cifs",   ref);
00212         addUrlByScheme("http",   ref);
00213         addUrlByScheme("https",  ref);
00214         ref->config("path_encode_slash2", "y"); // always encode 2. slash
00215         addUrlByScheme("ftp",    ref);
00216         addUrlByScheme("sftp",   ref);
00217         addUrlByScheme("tftp",   ref);
00218       }
00219 
00220       bool
00221       addUrlByScheme(const std::string &scheme,
00222                      UrlRef            urlImpl)
00223       {
00224         if( urlImpl && urlImpl->isValidScheme(scheme))
00225         {
00226           UrlRef ref(urlImpl);
00227           ref->clear();
00228           urlByScheme[str::toLower(scheme)] = ref;
00229           return true;
00230         }
00231         return false;
00232       }
00233 
00234       UrlRef
00235       getUrlByScheme(const std::string &scheme) const
00236       {
00237         UrlBySchemeMap::const_iterator i(urlByScheme.find(str::toLower(scheme)));
00238         if( i != urlByScheme.end())
00239         {
00240           return i->second;
00241         }
00242         return UrlRef();
00243       }
00244 
00245       bool
00246       isRegisteredScheme(const std::string &scheme) const
00247       {
00248         return urlByScheme.find(str::toLower(scheme)) != urlByScheme.end();
00249       }
00250 
00251       UrlSchemes
00252       getRegisteredSchemes() const
00253       {
00254         UrlBySchemeMap::const_iterator i(urlByScheme.begin());
00255         UrlSchemes                     schemes;
00256 
00257         schemes.reserve(urlByScheme.size());
00258         for( ; i != urlByScheme.end(); ++i)
00259         {
00260           schemes.push_back(i->first);
00261         }
00262         return schemes;
00263       }
00264     };
00265 
00266 
00267     // ---------------------------------------------------------------
00268     UrlByScheme & g_urlSchemeRepository()
00269     {
00270       static UrlByScheme _v;
00271       return _v;
00272     }
00273 
00275   } // anonymous namespace
00277 
00278 
00279   // -----------------------------------------------------------------
00280   Url::~Url()
00281   {
00282   }
00283 
00284 
00285   // -----------------------------------------------------------------
00286   Url::Url()
00287     : m_impl( new UrlBase())
00288   {
00289   }
00290 
00291 
00292   // -----------------------------------------------------------------
00293   Url::Url(const Url &url)
00294     : m_impl( url.m_impl)
00295   {
00296     if( !m_impl)
00297     {
00298       ZYPP_THROW(url::UrlException(
00299         _("Unable to clone Url object")
00300       ));
00301     }
00302   }
00303 
00304 
00305   // -----------------------------------------------------------------
00306   Url::Url(const zypp::url::UrlRef &url)
00307     : m_impl( url)
00308   {
00309     if( !m_impl)
00310     {
00311       ZYPP_THROW(url::UrlException(
00312         _("Invalid empty Url object reference")
00313       ));
00314     }
00315   }
00316 
00317 
00318   // -----------------------------------------------------------------
00319   Url::Url(const std::string &encodedUrl)
00320     : m_impl( parseUrl(encodedUrl))
00321   {
00322     if( !m_impl)
00323     {
00324       ZYPP_THROW(url::UrlParsingException(
00325         _("Unable to parse Url components")
00326       ));
00327     }
00328   }
00329 
00330 
00331   // -----------------------------------------------------------------
00332   Url&
00333   Url::operator = (const std::string &encodedUrl)
00334   {
00335     UrlRef url( parseUrl(encodedUrl));
00336     if( !url)
00337     {
00338       ZYPP_THROW(url::UrlParsingException(
00339         _("Unable to parse Url components")
00340       ));
00341     }
00342     m_impl = url;
00343     return *this;
00344   }
00345 
00346 
00347   // -----------------------------------------------------------------
00348   Url&
00349   Url::operator = (const Url &url)
00350   {
00351     m_impl = url.m_impl;
00352     return *this;
00353   }
00354 
00355 
00356   // -----------------------------------------------------------------
00357   // static
00358   bool
00359   Url::registerScheme(const std::string &scheme,
00360                       UrlRef            urlImpl)
00361   {
00362     return g_urlSchemeRepository().addUrlByScheme(scheme, urlImpl);
00363   }
00364 
00365 
00366   // -----------------------------------------------------------------
00367   // static
00368   UrlRef
00369   Url::parseUrl(const std::string &encodedUrl)
00370   {
00371     UrlRef      url;
00372     str::smatch out;
00373     bool        ret = false;
00374 
00375     try
00376     {
00377       str::regex  rex(RX_SPLIT_URL);
00378       ret = str::regex_match(encodedUrl, out, rex);
00379     }
00380     catch( ... )
00381     {}
00382 
00383     if(ret && out.size() == 5)
00384     {
00385       std::string scheme = out[1];
00386       if (scheme.size() > 1)
00387         scheme = scheme.substr(0, scheme.size()-1);
00388       std::string authority = out[2];
00389       if (authority.size() >= 2)
00390         authority = authority.substr(2);
00391       std::string query = out[4];
00392       if (query.size() > 1)
00393         query = query.substr(1);
00394       std::string fragment = out[5];
00395       if (fragment.size() > 1)
00396         fragment = fragment.substr(1);
00397 
00398       url = g_urlSchemeRepository().getUrlByScheme(scheme);
00399       if( !url)
00400       {
00401         url.reset( new UrlBase());
00402       }
00403       url->init(scheme, authority, out[3],
00404                 query, fragment);
00405     }
00406     return url;
00407   }
00408 
00409 
00410   // -----------------------------------------------------------------
00411   // static
00412   zypp::url::UrlSchemes
00413   Url::getRegisteredSchemes()
00414   {
00415     return g_urlSchemeRepository().getRegisteredSchemes();
00416   }
00417 
00418 
00419   // -----------------------------------------------------------------
00420   // static
00421   bool
00422   Url::isRegisteredScheme(const std::string &scheme)
00423   {
00424     return g_urlSchemeRepository().isRegisteredScheme(scheme);
00425   }
00426 
00427 
00428   // -----------------------------------------------------------------
00429   zypp::url::UrlSchemes
00430   Url::getKnownSchemes() const
00431   {
00432     return m_impl->getKnownSchemes();
00433   }
00434 
00435 
00436   // -----------------------------------------------------------------
00437   bool
00438   Url::isValidScheme(const std::string &scheme) const
00439   {
00440     return m_impl->isValidScheme(scheme);
00441   }
00442 
00443 
00445   namespace
00446   {
00447     inline bool isInList( const char ** begin_r, const char ** end_r, const std::string & scheme_r )
00448     {
00449       for ( ; begin_r != end_r; ++begin_r )
00450         if ( scheme_r == *begin_r )
00451           return true;
00452       return false;
00453     }
00454   }
00455   bool Url::schemeIsLocal( const std::string & scheme_r )
00456   {
00457     static const char * val[] = { "cd", "dvd", "dir", "hd", "iso", "file" };
00458     return isInList( arrayBegin(val), arrayEnd(val), scheme_r );
00459   }
00460 
00461   bool Url::schemeIsRemote( const std::string & scheme_r )
00462   {
00463     static const char * val[] = { "http", "https", "nfs", "nfs4", "smb", "cifs", "ftp", "sftp", "tftp" };
00464     return isInList( arrayBegin(val), arrayEnd(val), scheme_r );
00465   }
00466 
00467   bool Url::schemeIsVolatile( const std::string & scheme_r )
00468   {
00469     static const char * val[] = { "cd", "dvd" };
00470     return isInList( arrayBegin(val), arrayEnd(val), scheme_r );
00471   }
00472 
00473   bool Url::schemeIsDownloading( const std::string & scheme_r )
00474   {
00475     static const char * val[] = { "http", "https", "ftp", "sftp", "tftp" };
00476     return isInList( arrayBegin(val), arrayEnd(val), scheme_r );
00477   }
00479 
00480   // -----------------------------------------------------------------
00481   bool
00482   Url::isValid() const
00483   {
00484     return m_impl->isValid();
00485   }
00486 
00487 
00488   // -----------------------------------------------------------------
00489   std::string
00490   Url::asString() const
00491   {
00492     return m_impl->asString();
00493   }
00494 
00495 
00496   // -----------------------------------------------------------------
00497   std::string
00498   Url::asCompleteString() const
00499   {
00500     // make sure, all url components are included;
00501     // regardless of the current configuration...
00502     ViewOptions opts(getViewOptions() +
00503                      ViewOption::WITH_SCHEME +
00504                      ViewOption::WITH_USERNAME +
00505                      ViewOption::WITH_PASSWORD +
00506                      ViewOption::WITH_HOST +
00507                      ViewOption::WITH_PORT +
00508                      ViewOption::WITH_PATH_NAME +
00509                      ViewOption::WITH_PATH_PARAMS +
00510                      ViewOption::WITH_QUERY_STR +
00511                      ViewOption::WITH_FRAGMENT);
00512     return m_impl->asString(opts);
00513   }
00514 
00515 
00516   // -----------------------------------------------------------------
00517   std::string
00518   Url::asString(const ViewOptions &opts) const
00519   {
00520     return m_impl->asString(opts);
00521   }
00522 
00523 
00524   // -----------------------------------------------------------------
00525   std::string
00526   Url::getScheme() const
00527   {
00528     return m_impl->getScheme();
00529   }
00530 
00531 
00532   // -----------------------------------------------------------------
00533   std::string
00534   Url::getAuthority() const
00535   {
00536     return m_impl->getAuthority();
00537   }
00538 
00539   // -----------------------------------------------------------------
00540   std::string
00541   Url::getPathData() const
00542   {
00543     return m_impl->getPathData();
00544   }
00545 
00546 
00547   // -----------------------------------------------------------------
00548   std::string
00549   Url::getQueryString() const
00550   {
00551     return m_impl->getQueryString();
00552   }
00553 
00554 
00555   // -----------------------------------------------------------------
00556   std::string
00557   Url::getFragment(zypp::url::EEncoding eflag) const
00558   {
00559     return m_impl->getFragment(eflag);
00560   }
00561 
00562 
00563   // -----------------------------------------------------------------
00564   std::string
00565   Url::getUsername(EEncoding eflag) const
00566   {
00567     return m_impl->getUsername(eflag);
00568   }
00569 
00570 
00571   // -----------------------------------------------------------------
00572   std::string
00573   Url::getPassword(EEncoding eflag) const
00574   {
00575     return m_impl->getPassword(eflag);
00576   }
00577 
00578 
00579   // -----------------------------------------------------------------
00580   std::string
00581   Url::getHost(EEncoding eflag) const
00582   {
00583     return m_impl->getHost(eflag);
00584   }
00585 
00586 
00587   // -----------------------------------------------------------------
00588   std::string
00589   Url::getPort() const
00590   {
00591     return m_impl->getPort();
00592   }
00593 
00594 
00595   // -----------------------------------------------------------------
00596   std::string
00597   Url::getPathName(EEncoding eflag) const
00598   {
00599     return m_impl->getPathName(eflag);
00600   }
00601 
00602 
00603   // -----------------------------------------------------------------
00604   std::string
00605   Url::getPathParams() const
00606   {
00607     return m_impl->getPathParams();
00608   }
00609 
00610 
00611   // -----------------------------------------------------------------
00612   zypp::url::ParamVec
00613   Url::getPathParamsVec() const
00614   {
00615     return m_impl->getPathParamsVec();
00616   }
00617 
00618 
00619   // -----------------------------------------------------------------
00620   zypp::url::ParamMap
00621   Url::getPathParamsMap(EEncoding eflag) const
00622   {
00623     return m_impl->getPathParamsMap(eflag);
00624   }
00625 
00626 
00627   // -----------------------------------------------------------------
00628   std::string
00629   Url::getPathParam(const std::string &param, EEncoding eflag) const
00630   {
00631     return m_impl->getPathParam(param, eflag);
00632   }
00633 
00634 
00635   // -----------------------------------------------------------------
00636   zypp::url::ParamVec
00637   Url::getQueryStringVec() const
00638   {
00639     return m_impl->getQueryStringVec();
00640   }
00641 
00642 
00643   // -----------------------------------------------------------------
00644   zypp::url::ParamMap
00645   Url::getQueryStringMap(EEncoding eflag) const
00646   {
00647     return m_impl->getQueryStringMap(eflag);
00648   }
00649 
00650 
00651   // -----------------------------------------------------------------
00652   std::string
00653   Url::getQueryParam(const std::string &param, EEncoding eflag) const
00654   {
00655     return m_impl->getQueryParam(param, eflag);
00656   }
00657 
00658 
00659   // -----------------------------------------------------------------
00660   void
00661   Url::setScheme(const std::string &scheme)
00662   {
00663     if(scheme == m_impl->getScheme())
00664     {
00665       return;
00666     }
00667     if( m_impl->isKnownScheme(scheme))
00668     {
00669       m_impl->setScheme(scheme);
00670       return;
00671     }
00672 
00673     UrlRef url = g_urlSchemeRepository().getUrlByScheme(scheme);
00674     if( !url)
00675     {
00676       url.reset( new UrlBase());
00677     }
00678     url->init(
00679       scheme,
00680       m_impl->getAuthority(),
00681       m_impl->getPathData(),
00682       m_impl->getQueryString(),
00683       m_impl->getFragment(zypp::url::E_ENCODED)
00684     );
00685     m_impl = url;
00686   }
00687 
00688 
00689   // -----------------------------------------------------------------
00690   void
00691   Url::setAuthority(const std::string &authority)
00692   {
00693     m_impl->setAuthority(authority);
00694   }
00695 
00696 
00697   // -----------------------------------------------------------------
00698   void
00699   Url::setPathData(const std::string &pathdata)
00700   {
00701     m_impl->setPathData(pathdata);
00702   }
00703 
00704 
00705   // -----------------------------------------------------------------
00706   void
00707   Url::setQueryString(const std::string &querystr)
00708   {
00709     m_impl->setQueryString(querystr);
00710   }
00711 
00712 
00713   // -----------------------------------------------------------------
00714   void
00715   Url::setFragment(const std::string &fragment, EEncoding eflag)
00716   {
00717     m_impl->setFragment(fragment, eflag);
00718   }
00719 
00720 
00721   // -----------------------------------------------------------------
00722   void
00723   Url::setUsername(const std::string &user,
00724                    EEncoding         eflag)
00725   {
00726     m_impl->setUsername(user, eflag);
00727   }
00728 
00729 
00730   // -----------------------------------------------------------------
00731   void
00732   Url::setPassword(const std::string &pass,
00733                    EEncoding         eflag)
00734   {
00735     m_impl->setPassword(pass, eflag);
00736   }
00737 
00738 
00739   // -----------------------------------------------------------------
00740   void
00741   Url::setHost(const std::string &host)
00742   {
00743     m_impl->setHost(host);
00744   }
00745 
00746 
00747   // -----------------------------------------------------------------
00748   void
00749   Url::setPort(const std::string &port)
00750   {
00751     m_impl->setPort(port);
00752   }
00753 
00754 
00755   // -----------------------------------------------------------------
00756   void
00757   Url::setPathName(const std::string &path,
00758                    EEncoding         eflag)
00759   {
00760     m_impl->setPathName(path, eflag);
00761   }
00762 
00763 
00764   // -----------------------------------------------------------------
00765   void
00766   Url::setPathParams(const std::string &params)
00767   {
00768     m_impl->setPathParams(params);
00769   }
00770 
00771 
00772   // -----------------------------------------------------------------
00773   void
00774   Url::setPathParamsVec(const zypp::url::ParamVec &pvec)
00775   {
00776     m_impl->setPathParamsVec(pvec);
00777   }
00778 
00779 
00780   // -----------------------------------------------------------------
00781   void
00782   Url::setPathParamsMap(const zypp::url::ParamMap &pmap)
00783   {
00784     m_impl->setPathParamsMap(pmap);
00785   }
00786 
00787 
00788   // -----------------------------------------------------------------
00789   void
00790   Url::setPathParam(const std::string &param, const std::string &value)
00791   {
00792     m_impl->setPathParam(param, value);
00793   }
00794 
00795 
00796   // -----------------------------------------------------------------
00797   void
00798   Url::setQueryStringVec(const zypp::url::ParamVec &pvec)
00799   {
00800     m_impl->setQueryStringVec(pvec);
00801   }
00802 
00803 
00804   // -----------------------------------------------------------------
00805   void
00806   Url::setQueryStringMap(const zypp::url::ParamMap &pmap)
00807   {
00808     m_impl->setQueryStringMap(pmap);
00809   }
00810 
00811   // -----------------------------------------------------------------
00812   void
00813   Url::setQueryParam(const std::string &param, const std::string &value)
00814   {
00815     m_impl->setQueryParam(param, value);
00816   }
00817 
00818   // -----------------------------------------------------------------
00819   void
00820   Url::delQueryParam(const std::string &param)
00821   {
00822     m_impl->delQueryParam(param);
00823   }
00824 
00825   // -----------------------------------------------------------------
00826   ViewOptions
00827   Url::getViewOptions() const
00828   {
00829     return m_impl->getViewOptions();
00830   }
00831 
00832   // -----------------------------------------------------------------
00833   void
00834   Url::setViewOptions(const ViewOptions &vopts)
00835   {
00836     m_impl->setViewOptions(vopts);
00837   }
00838 
00839   // -----------------------------------------------------------------
00840   std::ostream & operator<<( std::ostream & str, const Url & url )
00841   {
00842     return str << url.asString();
00843   }
00844 
00845   bool operator<( const Url &lhs, const Url &rhs )
00846   {
00847     return (lhs.asCompleteString() < rhs.asCompleteString());
00848   }
00849 
00850   bool operator==( const Url &lhs, const Url &rhs )
00851   {
00852     return (lhs.asCompleteString() == rhs.asCompleteString());
00853   }
00854 
00855   bool operator!=( const Url &lhs, const Url &rhs )
00856   {
00857     return (lhs.asCompleteString() != rhs.asCompleteString());
00858   }
00859 
00861 } // namespace zypp
00863 /*
00864 ** vim: set ts=2 sts=2 sw=2 ai et:
00865 */

Generated on Tue May 5 14:43:20 2015 for libzypp by  doxygen 1.5.6