- fixed password handling in URLs (bug #347273)
[platform/upstream/libzypp.git] / zypp / url / UrlBase.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /**
10  * \file zypp/url/UrlBase.cc
11  */
12 #include <zypp/url/UrlBase.h>
13 #include <zypp/base/String.h>
14 #include <zypp/base/Gettext.h>
15 #include <zypp/base/Regex.h>
16
17 #include <stdexcept>
18 #include <climits>
19 #include <errno.h>
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <arpa/inet.h>
23
24 #include <iostream>
25
26 // in the Estonian locale, a-z excludes t, for example. #302525
27 // http://en.wikipedia.org/wiki/Estonian_alphabet
28 #define a_zA_Z "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
29
30 // ---------------------------------------------------------------
31 /*
32 ** authority = //[user [:password] @ ] host [:port]
33 **
34 ** host      = hostname | IPv4 | "[" IPv6-IP "]" | "[v...]"
35 */
36 #define RX_VALID_SCHEME    "^[" a_zA_Z "][" a_zA_Z "0-9\\.+-]*$"
37
38 #define RX_VALID_PORT      "^[0-9]{1,5}$"
39
40 #define RX_VALID_HOSTNAME  "^[[:alnum:]]+([\\.-][[:alnum:]]+)*$"
41
42 #define RX_VALID_HOSTIPV4  \
43         "^([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$"
44
45 #define RX_VALID_HOSTIPV6  \
46         "^\\[[:a-fA-F0-9]+(:[0-9]{1,3}(\\.[0-9]{1,3}){3})?\\]$"
47
48
49 //////////////////////////////////////////////////////////////////////
50 namespace zypp
51 { ////////////////////////////////////////////////////////////////////
52
53   ////////////////////////////////////////////////////////////////////
54   namespace url
55   { //////////////////////////////////////////////////////////////////
56
57
58     // ---------------------------------------------------------------
59     /*
60     ** URL asString() view option constants:
61     */
62     const ViewOption  ViewOption::WITH_SCHEME       = 0x0001;
63     const ViewOption  ViewOption::WITH_USERNAME     = 0x0002;
64     const ViewOption  ViewOption::WITH_PASSWORD     = 0x0004;
65     const ViewOption  ViewOption::WITH_HOST         = 0x0008;
66     const ViewOption  ViewOption::WITH_PORT         = 0x0010;
67     const ViewOption  ViewOption::WITH_PATH_NAME    = 0x0020;
68     const ViewOption  ViewOption::WITH_PATH_PARAMS  = 0x0040;
69     const ViewOption  ViewOption::WITH_QUERY_STR    = 0x0080;
70     const ViewOption  ViewOption::WITH_FRAGMENT     = 0x0100;
71     const ViewOption  ViewOption::EMPTY_AUTHORITY   = 0x0200;
72     const ViewOption  ViewOption::EMPTY_PATH_NAME   = 0x0400;
73     const ViewOption  ViewOption::EMPTY_PATH_PARAMS = 0x0800;
74     const ViewOption  ViewOption::EMPTY_QUERY_STR   = 0x1000;
75     const ViewOption  ViewOption::EMPTY_FRAGMENT    = 0x2000;
76     const ViewOption  ViewOption::DEFAULTS          = 0x07bb;
77     /*
78     const ViewOption  ViewOption::DEFAULTS          =
79                       ViewOption::WITH_SCHEME       +
80                       ViewOption::WITH_USERNAME     +
81                       ViewOption::WITH_HOST         +
82                       ViewOption::WITH_PORT         +
83                       ViewOption::WITH_PATH_NAME    +
84                       ViewOption::WITH_QUERY_STR    +
85                       ViewOption::WITH_FRAGMENT     +
86                       ViewOption::EMPTY_AUTHORITY   +
87                       ViewOption::EMPTY_PATH_NAME;
88     */
89
90     // ---------------------------------------------------------------
91     ViewOption::ViewOption()
92       : opt(0x07bb)
93     {}
94
95     // ---------------------------------------------------------------
96     ViewOption::ViewOption(int option)
97       : opt(option)
98     {}
99
100
101     // ---------------------------------------------------------------
102     /*
103     ** Behaviour configuration variables.
104     */
105     typedef std::map< std::string, std::string > UrlConfig;
106
107
108     // ---------------------------------------------------------------
109     /**
110      * \brief Internal data used by UrlBase.
111      */
112     class UrlBaseData
113     {
114     public:
115       UrlBaseData()
116       {}
117
118       UrlBaseData(const UrlConfig &conf)
119         : config(conf)
120       {}
121
122       UrlConfig       config;
123       ViewOptions     vopts;
124
125       std::string     scheme;
126       std::string     user;
127       std::string     pass;
128       std::string     host;
129       std::string     port;
130       std::string     pathname;
131       std::string     pathparams;
132       std::string     querystr;
133       std::string     fragment;
134     };
135
136
137     // ---------------------------------------------------------------
138     /*
139     ** Anonymous/internal utility namespace:
140     */
141     namespace // anonymous
142     {
143
144                         // -------------------------------------------------------------
145       inline void
146       checkUrlData(const std::string &data,
147                    const std::string &name,
148                    const std::string &regx,
149                    bool               show=true)
150       {
151         if( regx.empty() || regx == "^$")
152         {
153           ZYPP_THROW(UrlNotAllowedException(
154             str::form(_("Url scheme does not allow a %s"), name.c_str())
155           ));
156         }
157         else
158         {
159           bool valid = false;
160           try
161           {
162             str::regex rex(regx);
163             valid = str::regex_match(data, rex);
164           }
165           catch( ... )
166           {}
167
168           if( !valid)
169           {
170             if( show)
171             {
172               ZYPP_THROW(UrlBadComponentException(
173                 str::form(_("Invalid %s component '%s'"),
174                           name.c_str(), data.c_str())
175               ));
176             }
177             else
178             {
179               ZYPP_THROW(UrlBadComponentException(
180                 str::form(_("Invalid %s component"), name.c_str())
181               ));
182             }
183           }
184         }
185       }
186
187     } // namespace
188
189
190     // ---------------------------------------------------------------
191     UrlBase::~UrlBase()
192     {
193       delete m_data;
194       m_data = NULL;
195     }
196
197
198     // ---------------------------------------------------------------
199     UrlBase::UrlBase()
200       : m_data( new UrlBaseData())
201     {
202       configure();
203     }
204
205
206     // ---------------------------------------------------------------
207     UrlBase::UrlBase(const UrlBase &url)
208       : m_data( new UrlBaseData( *(url.m_data)))
209     {
210     }
211
212
213     // ---------------------------------------------------------------
214     UrlBase::UrlBase(const std::string &scheme,
215                      const std::string &authority,
216                      const std::string &pathdata,
217                      const std::string &querystr,
218                      const std::string &fragment)
219       : m_data( new UrlBaseData())
220     {
221       configure();
222       init(scheme, authority, pathdata, querystr, fragment);
223     }
224
225
226     // ---------------------------------------------------------------
227     void
228     UrlBase::init(const std::string &scheme,
229                   const std::string &authority,
230                   const std::string &pathdata,
231                   const std::string &querystr,
232                   const std::string &fragment)
233     {
234       setScheme(scheme);
235       setAuthority(authority);
236       setPathData(pathdata);
237       setQueryString(querystr);
238       setFragment(fragment, zypp::url::E_ENCODED);
239     }
240
241
242     // ---------------------------------------------------------------
243     void
244     UrlBase::configure()
245     {
246       config("sep_pathparams",  ";");
247       config("psep_pathparam",  ",");
248       config("vsep_pathparam",  "=");
249
250       config("psep_querystr",   "&");
251       config("vsep_querystr",   "=");
252
253       config("safe_username",   "~!$&'()*+=,;");
254       config("safe_password",   "~!$&'()*+=,:;");
255       config("safe_hostname",   "[:]");
256       config("safe_pathname",   "~!$&'()*+=,:@/");
257       config("safe_pathparams", "~!$&'()*+=,:;@/");
258       config("safe_querystr",   "~!$&'()*+=,:;@/?");
259       config("safe_fragment",   "~!$&'()*+=,:;@/?");
260
261       // y=yes (allowed)
262       // n=no  (disallowed, exception if !empty)
263       config("with_authority",  "y");
264       config("with_port",       "y");
265
266       // y=yes (required but don't throw if empty)
267       // n=no  (not required, ignore if empty)
268       // m=mandatory (exception if empty)
269       config("require_host",    "n");
270       config("require_pathname","n");
271
272       // y=yes (encode 2. slash even if authority present)
273       // n=no  (don't encode 2. slash if authority present)
274       config("path_encode_slash2", "n");
275
276       config("rx_username",     "^([" a_zA_Z "0-9!$&'\\(\\)*+=,;~\\._-]|%[a-fA-F0-9]{2})+$");
277       config("rx_password",     "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:;~\\._-]|%[a-fA-F0-9]{2})+$");
278
279       config("rx_pathname",     "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:@/~\\._-]|%[a-fA-F0-9]{2})+$");
280       config("rx_pathparams",   "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:;@/~\\._-]|%[a-fA-F0-9]{2})+$");
281
282       config("rx_querystr",     "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:;@/?~\\._-]|%[a-fA-F0-9]{2})+$");
283       config("rx_fragment",     "^([" a_zA_Z "0-9!$&'\\(\\)*+=,:;@/?~\\._-]|%[a-fA-F0-9]{2})+$");
284     }
285
286
287     // ---------------------------------------------------------------
288     void
289     UrlBase::config(const std::string &opt, const std::string &val)
290     {
291       m_data->config[opt] = val;
292     }
293
294
295     // ---------------------------------------------------------------
296     std::string
297     UrlBase::config(const std::string &opt) const
298     {
299       UrlConfig::const_iterator v( m_data->config.find(opt));
300       if( v != m_data->config.end())
301         return v->second;
302       else
303         return std::string();
304     }
305
306
307     // ---------------------------------------------------------------
308     ViewOptions
309     UrlBase::getViewOptions() const
310     {
311       return m_data->vopts;
312     }
313
314
315     // ---------------------------------------------------------------
316     void
317     UrlBase::setViewOptions(const ViewOptions &vopts)
318     {
319         m_data->vopts = vopts;
320     }
321
322
323     // ---------------------------------------------------------------
324     void
325     UrlBase::clear()
326     {
327       zypp::url::UrlConfig   config(m_data->config);
328       zypp::url::ViewOptions vopts(m_data->vopts);
329       *m_data = UrlBaseData();
330       m_data->config = config;
331       m_data->vopts  = vopts;
332     }
333
334
335     // ---------------------------------------------------------------
336     UrlBase *
337     UrlBase::clone() const
338     {
339       return new UrlBase(*this);
340     }
341
342
343     // ---------------------------------------------------------------
344     zypp::url::UrlSchemes
345     UrlBase::getKnownSchemes() const
346     {
347       return UrlSchemes();
348     }
349
350
351     // ---------------------------------------------------------------
352     bool
353     UrlBase::isKnownScheme(const std::string &scheme) const
354     {
355       std::string                lscheme( str::toLower(scheme));
356       UrlSchemes                 schemes( getKnownSchemes());
357       UrlSchemes::const_iterator s;
358
359       for(s=schemes.begin(); s!=schemes.end(); ++s)
360       {
361         if( lscheme == str::toLower(*s))
362           return true;
363       }
364       return false;
365     }
366
367
368     // ---------------------------------------------------------------
369     bool
370     UrlBase::isValidScheme(const std::string &scheme) const
371     {
372       bool valid = false;
373       try
374       {
375         str::regex rex(RX_VALID_SCHEME);
376         valid = str::regex_match(scheme, rex);
377       }
378       catch( ... )
379       {}
380
381       if(valid)
382       {
383         std::string    lscheme( str::toLower(scheme));
384         UrlSchemes     schemes( getKnownSchemes());
385
386         if( schemes.empty())
387           return true;
388
389         UrlSchemes::const_iterator s;
390         for(s=schemes.begin(); s!=schemes.end(); ++s)
391         {
392           if( lscheme == str::toLower(*s))
393             return true;
394         }
395       }
396       return false;
397     }
398
399
400     // ---------------------------------------------------------------
401     bool
402     UrlBase::isValid() const
403     {
404       /*
405       ** scheme is the only mandatory component
406       ** for all url's and is already verified,
407       ** (except for empty Url instances), so
408       ** Url with empty scheme is never valid.
409       */
410       if( getScheme().empty())
411         return false;
412
413       std::string host( getHost(zypp::url::E_ENCODED));
414       if( host.empty() && config("require_host")     != "n")
415         return false;
416
417       std::string path( getPathName(zypp::url::E_ENCODED));
418       if( path.empty() && config("require_pathname") != "n")
419         return false;
420
421       /*
422       ** path has to begin with "/" if authority avaliable
423       ** if host is set after the pathname, we can't throw
424       */
425       if( !host.empty() && !path.empty() && path.at(0) != '/')
426         return false;
427
428       return true;
429     }
430
431
432     // ---------------------------------------------------------------
433     std::string
434     UrlBase::asString() const
435     {
436       return asString(getViewOptions());
437     }
438
439
440     // ---------------------------------------------------------------
441     std::string
442     UrlBase::asString(const zypp::url::ViewOptions &opts) const
443     {
444       std::string   url;
445       UrlBaseData   tmp;
446
447       if( opts.has(ViewOptions::WITH_SCHEME))
448       {
449         tmp.scheme = getScheme();
450         if( !tmp.scheme.empty())
451         {
452           url += tmp.scheme + ":";
453
454           if( opts.has(ViewOptions::WITH_HOST))
455           {
456             tmp.host = getHost(zypp::url::E_ENCODED);
457             if( !tmp.host.empty())
458             {
459               url += "//";
460
461               if( opts.has(ViewOptions::WITH_USERNAME))
462               {
463                 tmp.user = getUsername(zypp::url::E_ENCODED);
464                 if( !tmp.user.empty())
465                 {
466                   url += tmp.user;
467
468                   if( opts.has(ViewOptions::WITH_PASSWORD))
469                   {
470                     tmp.pass = getPassword(zypp::url::E_ENCODED);
471                     if( !tmp.pass.empty())
472                     {
473                       url += ":" + tmp.pass;
474                     }
475                   }
476                   url += "@";
477                 }
478               }
479
480               url += tmp.host;
481
482               if( opts.has(ViewOptions::WITH_PORT))
483               {
484                 tmp.port = getPort();
485                 if( !tmp.port.empty())
486                 {
487                   url += ":" + tmp.port;
488                 }
489               }
490             }
491             else if( opts.has(ViewOptions::EMPTY_AUTHORITY))
492             {
493               url += "//";
494             }
495           }
496           else if( opts.has(ViewOptions::EMPTY_AUTHORITY))
497           {
498             url += "//";
499           }
500         }
501       }
502
503       if( opts.has(ViewOptions::WITH_PATH_NAME))
504       {
505         tmp.pathname = getPathName(zypp::url::E_ENCODED);
506         if( !tmp.pathname.empty())
507         {
508           if(url.find("/") != std::string::npos)
509           {
510             // Url contains authority (that may be empty),
511             // we may need a rewrite of the encoded path.
512             tmp.pathname = cleanupPathName(tmp.pathname, true);
513             if(tmp.pathname.at(0) != '/')
514             {
515               url += "/";
516             }
517           }
518           url += tmp.pathname;
519
520           if( opts.has(ViewOptions::WITH_PATH_PARAMS))
521           {
522             tmp.pathparams = getPathParams();
523             if( !tmp.pathparams.empty())
524             {
525               url += ";" + tmp.pathparams;
526             }
527             else if( opts.has(ViewOptions::EMPTY_PATH_PARAMS))
528             {
529               url += ";";
530             }
531           }
532         }
533         else if( opts.has(ViewOptions::EMPTY_PATH_NAME)
534                  && url.find("/") != std::string::npos)
535         {
536           url += "/";
537           if( opts.has(ViewOptions::EMPTY_PATH_PARAMS))
538           {
539             url += ";";
540           }
541         }
542       }
543
544       if( opts.has(ViewOptions::WITH_QUERY_STR))
545       {
546         tmp.querystr = getQueryString();
547         if( !tmp.querystr.empty())
548         {
549           url += "?" + tmp.querystr;
550         }
551         else if( opts.has(ViewOptions::EMPTY_QUERY_STR))
552         {
553           url += "?";
554         }
555       }
556
557       if( opts.has(ViewOptions::WITH_FRAGMENT))
558       {
559         tmp.fragment = getFragment(zypp::url::E_ENCODED);
560         if( !tmp.fragment.empty())
561         {
562           url += "#" + tmp.fragment;
563         }
564         else if( opts.has(ViewOptions::EMPTY_FRAGMENT))
565         {
566           url += "#";
567         }
568       }
569
570       return url;
571     }
572
573
574     // ---------------------------------------------------------------
575     std::string
576     UrlBase::getScheme() const
577     {
578       return m_data->scheme;
579     }
580
581
582     // ---------------------------------------------------------------
583     std::string
584     UrlBase::getAuthority() const
585     {
586       std::string str;
587       if( !getHost(zypp::url::E_ENCODED).empty())
588       {
589         if( !getUsername(zypp::url::E_ENCODED).empty())
590         {
591           str = getUsername(zypp::url::E_ENCODED);
592           if( !getPassword(zypp::url::E_ENCODED).empty())
593           {
594             str += ":" + getPassword(zypp::url::E_ENCODED);
595           }
596           str += "@";
597         }
598
599         str += getHost(zypp::url::E_ENCODED);
600         if( !getPort().empty())
601         {
602           str += ":" + getPort();
603         }
604       }
605       return str;
606     }
607
608
609     // ---------------------------------------------------------------
610     std::string
611     UrlBase::getPathData() const
612     {
613       return getPathName(zypp::url::E_ENCODED) +
614              config("sep_pathparams") +
615              getPathParams();
616     }
617
618
619     // ---------------------------------------------------------------
620     std::string
621     UrlBase::getQueryString() const
622     {
623       return m_data->querystr;
624     }
625
626
627     // ---------------------------------------------------------------
628     std::string
629     UrlBase::getFragment(EEncoding eflag) const
630     {
631       if(eflag == zypp::url::E_DECODED)
632         return zypp::url::decode(m_data->fragment);
633       else
634         return m_data->fragment;
635     }
636
637
638     // ---------------------------------------------------------------
639     std::string
640     UrlBase::getUsername(EEncoding eflag) const
641     {
642       if(eflag == zypp::url::E_DECODED)
643         return zypp::url::decode(m_data->user);
644       else
645         return m_data->user;
646     }
647
648
649     // ---------------------------------------------------------------
650     std::string
651     UrlBase::getPassword(EEncoding eflag) const
652     {
653       if(eflag == zypp::url::E_DECODED)
654         return zypp::url::decode(m_data->pass);
655       else
656         return m_data->pass;
657     }
658
659
660     // ---------------------------------------------------------------
661     std::string
662     UrlBase::getHost(EEncoding eflag) const
663     {
664       if(eflag == zypp::url::E_DECODED)
665         return zypp::url::decode(m_data->host);
666       else
667         return m_data->host;
668     }
669
670
671     // ---------------------------------------------------------------
672     std::string
673     UrlBase::getPort() const
674     {
675       return m_data->port;
676     }
677
678
679     // ---------------------------------------------------------------
680     std::string
681     UrlBase::getPathName(EEncoding eflag) const
682     {
683       if(eflag == zypp::url::E_DECODED)
684         return zypp::url::decode(m_data->pathname);
685       else
686         return cleanupPathName(m_data->pathname);
687     }
688
689
690     // ---------------------------------------------------------------
691     std::string
692     UrlBase::getPathParams() const
693     {
694       return m_data->pathparams;
695     }
696
697
698     // ---------------------------------------------------------------
699     zypp::url::ParamVec
700     UrlBase::getPathParamsVec() const
701     {
702       zypp::url::ParamVec pvec;
703       if( config("psep_pathparam").empty())
704       {
705         pvec.push_back(getPathParams());
706       }
707       else
708       {
709         zypp::url::split(
710           pvec,
711           getPathParams(),
712           config("psep_pathparam")
713         );
714       }
715       return pvec;
716     }
717
718
719     // ---------------------------------------------------------------
720     zypp::url::ParamMap
721     UrlBase::getPathParamsMap(EEncoding eflag) const
722     {
723       if( config("psep_pathparam").empty() ||
724           config("vsep_pathparam").empty())
725       {
726         ZYPP_THROW(UrlNotSupportedException(
727           _("Path parameter parsing not supported for this URL")
728         ));
729       }
730       zypp::url::ParamMap pmap;
731       zypp::url::split(
732         pmap,
733         getPathParams(),
734         config("psep_pathparam"),
735         config("vsep_pathparam"),
736         eflag
737       );
738       return pmap;
739     }
740
741
742     // ---------------------------------------------------------------
743     std::string
744     UrlBase::getPathParam(const std::string &param, EEncoding eflag) const
745     {
746       zypp::url::ParamMap pmap( getPathParamsMap( eflag));
747       zypp::url::ParamMap::const_iterator i( pmap.find(param));
748
749       return i != pmap.end() ? i->second : std::string();
750     }
751
752
753     // ---------------------------------------------------------------
754     zypp::url::ParamVec
755     UrlBase::getQueryStringVec() const
756     {
757       zypp::url::ParamVec pvec;
758       if( config("psep_querystr").empty())
759       {
760         pvec.push_back(getQueryString());
761       }
762       else
763       {
764         zypp::url::split(
765           pvec,
766           getQueryString(),
767           config("psep_querystr")
768         );
769       }
770       return pvec;
771     }
772
773
774     // ---------------------------------------------------------------
775     zypp::url::ParamMap
776     UrlBase::getQueryStringMap(EEncoding eflag) const
777     {
778       if( config("psep_querystr").empty() ||
779           config("vsep_querystr").empty())
780       {
781         ZYPP_THROW(UrlNotSupportedException(
782           _("Query string parsing not supported for this URL")
783         ));
784       }
785       zypp::url::ParamMap pmap;
786       zypp::url::split(
787         pmap,
788         getQueryString(),
789         config("psep_querystr"),
790         config("vsep_querystr"),
791         eflag
792       );
793       return pmap;
794     }
795
796
797     // ---------------------------------------------------------------
798     std::string
799     UrlBase::getQueryParam(const std::string &param, EEncoding eflag) const
800     {
801       zypp::url::ParamMap pmap( getQueryStringMap( eflag));
802       zypp::url::ParamMap::const_iterator i( pmap.find(param));
803
804       return i != pmap.end() ? i->second : std::string();
805     }
806
807
808     // ---------------------------------------------------------------
809     void
810     UrlBase::setScheme(const std::string &scheme)
811     {
812       if( isValidScheme(scheme))
813       {
814         m_data->scheme = str::toLower(scheme);
815       }
816       else
817       if( scheme.empty())
818       {
819         ZYPP_THROW(UrlBadComponentException(
820           _("Url scheme is a required component")
821         ));
822       }
823       else
824       {
825         ZYPP_THROW(UrlBadComponentException(
826           str::form(_("Invalid Url scheme '%s'"), scheme.c_str())
827         ));
828       }
829     }
830
831
832     // ---------------------------------------------------------------
833     void
834     UrlBase::setAuthority(const std::string &authority)
835     {
836       std::string s = authority;
837       std::string::size_type p,q;
838
839       std::string username, password, host, port;
840
841       if ((p=s.find('@')) != std::string::npos)
842       {
843         q = s.find(':');
844         if (q != std::string::npos && q < p)
845         {
846           setUsername(s.substr(0, q), zypp::url::E_ENCODED);
847           setPassword(s.substr(q+1, p-q-1), zypp::url::E_ENCODED);
848         }
849         else
850           setUsername(s.substr(0, p), zypp::url::E_ENCODED);
851         s = s.substr(p+1);
852       }
853       q = s.rfind(']');
854       if ((p = s.rfind(':')) != std::string::npos && p > q+1)
855       {
856
857         setHost(s.substr(0, p));
858         setPort(s.substr(p+1));
859       }
860       else
861         setHost(s);
862     }
863
864     // ---------------------------------------------------------------
865     void
866     UrlBase::setPathData(const std::string &pathdata)
867     {
868       size_t      pos = std::string::npos;
869       std::string sep(config("sep_pathparams"));
870
871       if( !sep.empty())
872         pos = pathdata.find(sep);
873
874       if( pos != std::string::npos)
875       {
876         setPathName(pathdata.substr(0, pos),
877                     zypp::url::E_ENCODED);
878         setPathParams(pathdata.substr(pos + 1));
879       }
880       else
881       {
882         setPathName(pathdata,
883                     zypp::url::E_ENCODED);
884         setPathParams("");
885       }
886     }
887
888
889     // ---------------------------------------------------------------
890     void
891     UrlBase::setQueryString(const std::string &querystr)
892     {
893       if( querystr.empty())
894       {
895         m_data->querystr = querystr;
896       }
897       else
898       {
899         checkUrlData(querystr, "query string", config("rx_querystr"));
900
901         m_data->querystr = querystr;
902       }
903     }
904
905
906     // ---------------------------------------------------------------
907     void
908     UrlBase::setFragment(const std::string &fragment,
909                          EEncoding         eflag)
910     {
911       if( fragment.empty())
912       {
913         m_data->fragment = fragment;
914       }
915       else
916       {
917         if(eflag == zypp::url::E_ENCODED)
918         {
919           checkUrlData(fragment, "fragment", config("rx_fragment"));
920
921           m_data->fragment = fragment;
922         }
923         else
924         {
925           m_data->fragment = zypp::url::encode(
926             fragment, config("safe_fragment")
927           );
928         }
929       }
930     }
931
932
933     // ---------------------------------------------------------------
934     void
935     UrlBase::setUsername(const std::string &user,
936                          EEncoding         eflag)
937     {
938       if( user.empty())
939       {
940         m_data->user = user;
941       }
942       else
943       {
944         if( config("with_authority") != "y")
945         {
946           ZYPP_THROW(UrlNotAllowedException(
947             _("Url scheme does not allow a username")
948           ));
949         }
950
951         if(eflag == zypp::url::E_ENCODED)
952         {
953           checkUrlData(user, "username", config("rx_username"));
954
955           m_data->user = user;
956         }
957         else
958         {
959           m_data->user = zypp::url::encode(
960             user, config("safe_username")
961           );
962         }
963       }
964     }
965
966
967     // ---------------------------------------------------------------
968     void
969     UrlBase::setPassword(const std::string &pass,
970                          EEncoding         eflag)
971     {
972       if( pass.empty())
973       {
974         m_data->pass = pass;
975       }
976       else
977       {
978         if( config("with_authority") != "y")
979         {
980           ZYPP_THROW(UrlNotAllowedException(
981             _("Url scheme does not allow a password")
982           ));
983         }
984
985         if(eflag == zypp::url::E_ENCODED)
986         {
987           checkUrlData(pass, "password", config("rx_password"), false);
988
989           m_data->pass = pass;
990         }
991         else
992         {
993           m_data->pass = zypp::url::encode(
994             pass, config("safe_password")
995           );
996         }
997       }
998     }
999
1000
1001     // ---------------------------------------------------------------
1002     void
1003     UrlBase::setHost(const std::string &host)
1004     {
1005       if( host.empty())
1006       {
1007         if(config("require_host") == "m")
1008         {
1009           ZYPP_THROW(UrlNotAllowedException(
1010             _("Url scheme requires a host component")
1011           ));
1012         }
1013         m_data->host = host;
1014       }
1015       else
1016       {
1017         if( config("with_authority") != "y")
1018         {
1019           ZYPP_THROW(UrlNotAllowedException(
1020             _("Url scheme does not allow a host component")
1021           ));
1022         }
1023
1024         if( isValidHost(host))
1025         {
1026           std::string temp;
1027
1028           // always decode in case isValidHost()
1029           // is reimplemented and supports also
1030           // the [v ... ] notation.
1031           if( host.at(0) == '[')
1032           {
1033             temp = str::toUpper(zypp::url::decode(host));
1034           }
1035           else
1036           {
1037             temp = str::toLower(zypp::url::decode(host));
1038           }
1039
1040           m_data->host = zypp::url::encode(
1041             temp, config("safe_hostname")
1042           );
1043         }
1044         else
1045         {
1046           ZYPP_THROW(UrlBadComponentException(
1047             str::form(_("Invalid host component '%s'"), host.c_str())
1048           ));
1049         }
1050       }
1051     }
1052
1053
1054     // ---------------------------------------------------------------
1055     void
1056     UrlBase::setPort(const std::string &port)
1057     {
1058       if( port.empty())
1059       {
1060         m_data->port = port;
1061       }
1062       else
1063       {
1064         if( config("with_authority") != "y" ||
1065             config("with_port")      != "y")
1066         {
1067           ZYPP_THROW(UrlNotAllowedException(
1068             _("Url scheme does not allow a port")
1069           ));
1070         }
1071
1072         if( isValidPort(port))
1073         {
1074           m_data->port = port;
1075         }
1076         else
1077         {
1078           ZYPP_THROW(UrlBadComponentException(
1079             str::form(_("Invalid port component '%s'"), port.c_str())
1080           ));
1081         }
1082       }
1083     }
1084
1085
1086     // ---------------------------------------------------------------
1087     void
1088     UrlBase::setPathName(const std::string &path,
1089                          EEncoding         eflag)
1090     {
1091       if( path.empty())
1092       {
1093         if(config("require_pathname") == "m")
1094         {
1095           ZYPP_THROW(UrlNotAllowedException(
1096             _("Url scheme requires path name")
1097           ));
1098         }
1099         m_data->pathname = path;
1100       }
1101       else
1102       {
1103         if(eflag == zypp::url::E_ENCODED)
1104         {
1105           checkUrlData(path, "path name", config("rx_pathname"));
1106
1107           if( !getHost(zypp::url::E_ENCODED).empty())
1108           {
1109             // has to begin with a "/". For consistency with
1110             // setPathName while the host is empty, we allow
1111             // it in encoded ("%2f") form - cleanupPathName()
1112             // will fix / decode the first slash if needed.
1113             if(!(path.at(0) == '/' || (path.size() >= 3 &&
1114                  str::toLower(path.substr(0, 3)) == "%2f")))
1115             {
1116               ZYPP_THROW(UrlNotAllowedException(
1117                 _("Relative path not allowed if authority exists")
1118               ));
1119             }
1120           }
1121
1122           m_data->pathname = cleanupPathName(path);
1123         }
1124         else //     zypp::url::E_DECODED
1125         {
1126           if( !getHost(zypp::url::E_ENCODED).empty())
1127           {
1128             if(path.at(0) != '/')
1129             {
1130               ZYPP_THROW(UrlNotAllowedException(
1131                 _("Relative path not allowed if authority exists")
1132               ));
1133             }
1134           }
1135
1136           m_data->pathname = cleanupPathName(
1137             zypp::url::encode(
1138               path, config("safe_pathname")
1139             )
1140           );
1141         }
1142       }
1143     }
1144
1145
1146     // ---------------------------------------------------------------
1147     void
1148     UrlBase::setPathParams(const std::string &params)
1149     {
1150       if( params.empty())
1151       {
1152         m_data->pathparams = params;
1153       }
1154       else
1155       {
1156         checkUrlData(params, "path parameters", config("rx_pathparams"));
1157
1158         m_data->pathparams = params;
1159       }
1160     }
1161
1162
1163     // ---------------------------------------------------------------
1164     void
1165     UrlBase::setPathParamsVec(const zypp::url::ParamVec &pvec)
1166     {
1167       setPathParams(
1168         zypp::url::join(
1169           pvec,
1170           config("psep_pathparam")
1171         )
1172       );
1173     }
1174
1175
1176     // ---------------------------------------------------------------
1177     void
1178     UrlBase::setPathParamsMap(const zypp::url::ParamMap &pmap)
1179     {
1180       if( config("psep_pathparam").empty() ||
1181           config("vsep_pathparam").empty())
1182       {
1183         ZYPP_THROW(UrlNotSupportedException(
1184           _("Path Parameter parsing not supported for this URL")
1185         ));
1186       }
1187       setPathParams(
1188         zypp::url::join(
1189           pmap,
1190           config("psep_pathparam"),
1191           config("vsep_pathparam"),
1192           config("safe_pathparams")
1193         )
1194       );
1195     }
1196
1197
1198     // ---------------------------------------------------------------
1199     void
1200     UrlBase::setPathParam(const std::string &param, const std::string &value)
1201     {
1202           zypp::url::ParamMap pmap( getPathParamsMap(zypp::url::E_DECODED));
1203           pmap[param] = value;
1204           setPathParamsMap(pmap);
1205     }
1206
1207
1208     // ---------------------------------------------------------------
1209     void
1210     UrlBase::setQueryStringVec(const zypp::url::ParamVec &pvec)
1211     {
1212       setQueryString(
1213         zypp::url::join(
1214           pvec,
1215           config("psep_querystr")
1216         )
1217       );
1218     }
1219
1220
1221     // ---------------------------------------------------------------
1222     void
1223     UrlBase::setQueryStringMap(const zypp::url::ParamMap &pmap)
1224     {
1225       if( config("psep_querystr").empty() ||
1226           config("vsep_querystr").empty())
1227       {
1228         ZYPP_THROW(UrlNotSupportedException(
1229           _("Query string parsing not supported for this URL")
1230         ));
1231       }
1232       setQueryString(
1233         zypp::url::join(
1234           pmap,
1235           config("psep_querystr"),
1236           config("vsep_querystr"),
1237           config("safe_querystr")
1238         )
1239       );
1240     }
1241
1242     // ---------------------------------------------------------------
1243     void
1244     UrlBase::setQueryParam(const std::string &param, const std::string &value)
1245     {
1246           zypp::url::ParamMap pmap( getQueryStringMap(zypp::url::E_DECODED));
1247           pmap[param] = value;
1248           setQueryStringMap(pmap);
1249     }
1250
1251
1252     // ---------------------------------------------------------------
1253     std::string
1254     UrlBase::cleanupPathName(const std::string &path) const
1255     {
1256       bool authority = !getHost(zypp::url::E_ENCODED).empty();
1257       return cleanupPathName(path, authority);
1258     }
1259
1260     // ---------------------------------------------------------------
1261     std::string
1262     UrlBase::cleanupPathName(const std::string &path, bool authority) const
1263     {
1264       std::string copy( path);
1265
1266       // decode the first slash if it is encoded ...
1267       if(copy.size() >= 3 && copy.at(0) != '/' &&
1268          str::toLower(copy.substr(0, 3)) == "%2f")
1269       {
1270         copy.replace(0, 3, "/");
1271       }
1272
1273       // if path begins with a double slash ("//"); encode the second
1274       // slash [minimal and IMO sufficient] before the first path
1275       // segment, to fulfill the path-absolute rule of RFC 3986
1276       // disallowing a "//" if no authority is present.
1277       if( authority)
1278       {
1279         //
1280         // rewrite of "//" to "/%2f" not required, use config
1281         //
1282         if(config("path_encode_slash2") == "y")
1283         {
1284           // rewrite "//" ==> "/%2f"
1285           if(copy.size() >= 2 && copy.at(0) == '/' && copy.at(1) == '/')
1286           {
1287             copy.replace(1, 1, "%2F");
1288           }
1289         }
1290         else
1291         {
1292           // rewrite "/%2f" ==> "//"
1293           if(copy.size() >= 4 && copy.at(0) == '/' &&
1294              str::toLower(copy.substr(1, 4)) == "%2f")
1295           {
1296             copy.replace(1, 4, "/");
1297           }
1298         }
1299       }
1300       else
1301       {
1302         // rewrite of "//" to "/%2f" is required (no authority)
1303         if(copy.size() >= 2 && copy.at(0) == '/' && copy.at(1) == '/')
1304         {
1305           copy.replace(1, 1, "%2F");
1306         }
1307       }
1308       return copy;
1309     }
1310
1311
1312     // ---------------------------------------------------------------
1313     bool
1314     UrlBase::isValidHost(const std::string &host) const
1315     {
1316       try
1317       {
1318         str::regex regx(RX_VALID_HOSTIPV6);
1319         if( str::regex_match(host, regx))
1320         {
1321           struct in6_addr ip;
1322           std::string temp( host.substr(1, host.size()-2));
1323
1324           return inet_pton(AF_INET6, temp.c_str(), &ip) > 0;
1325         }
1326         else
1327         {
1328           // matches also IPv4 dotted-decimal adresses...
1329           std::string temp( zypp::url::decode(host));
1330           str::regex  regx(RX_VALID_HOSTNAME);
1331           return str::regex_match(temp, regx);
1332         }
1333       }
1334       catch( ... )
1335       {}
1336
1337       return false;
1338     }
1339
1340
1341     // ---------------------------------------------------------------
1342     bool
1343     UrlBase::isValidPort(const std::string &port) const
1344     {
1345       try
1346       {
1347         str::regex regx(RX_VALID_PORT);
1348         if( str::regex_match(port, regx))
1349         {
1350           long pnum = str::strtonum<long>(port);
1351           return ( pnum >= 1 && pnum <= USHRT_MAX);
1352         }
1353       }
1354       catch( ... )
1355       {}
1356       return false;
1357     }
1358
1359
1360     //////////////////////////////////////////////////////////////////
1361   } // namespace url
1362   ////////////////////////////////////////////////////////////////////
1363
1364   ////////////////////////////////////////////////////////////////////
1365 } // namespace zypp
1366 //////////////////////////////////////////////////////////////////////
1367 /*
1368 ** vim: set ts=2 sts=2 sw=2 ai et:
1369 */