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