34ab0bd3b3ae26cdb1c53c9ade5717c5c077cd29
[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       if ((p = s.rfind(':')) != std::string::npos && ( (q = s.rfind(']')) == std::string::npos || q < p) )
854       {
855         setHost(s.substr(0, p));
856         setPort(s.substr(p+1));
857       }
858       else
859         setHost(s);
860     }
861
862     // ---------------------------------------------------------------
863     void
864     UrlBase::setPathData(const std::string &pathdata)
865     {
866       size_t      pos = std::string::npos;
867       std::string sep(config("sep_pathparams"));
868
869       if( !sep.empty())
870         pos = pathdata.find(sep);
871
872       if( pos != std::string::npos)
873       {
874         setPathName(pathdata.substr(0, pos),
875                     zypp::url::E_ENCODED);
876         setPathParams(pathdata.substr(pos + 1));
877       }
878       else
879       {
880         setPathName(pathdata,
881                     zypp::url::E_ENCODED);
882         setPathParams("");
883       }
884     }
885
886
887     // ---------------------------------------------------------------
888     void
889     UrlBase::setQueryString(const std::string &querystr)
890     {
891       if( querystr.empty())
892       {
893         m_data->querystr = querystr;
894       }
895       else
896       {
897         checkUrlData(querystr, "query string", config("rx_querystr"));
898
899         m_data->querystr = querystr;
900       }
901     }
902
903
904     // ---------------------------------------------------------------
905     void
906     UrlBase::setFragment(const std::string &fragment,
907                          EEncoding         eflag)
908     {
909       if( fragment.empty())
910       {
911         m_data->fragment = fragment;
912       }
913       else
914       {
915         if(eflag == zypp::url::E_ENCODED)
916         {
917           checkUrlData(fragment, "fragment", config("rx_fragment"));
918
919           m_data->fragment = fragment;
920         }
921         else
922         {
923           m_data->fragment = zypp::url::encode(
924             fragment, config("safe_fragment")
925           );
926         }
927       }
928     }
929
930
931     // ---------------------------------------------------------------
932     void
933     UrlBase::setUsername(const std::string &user,
934                          EEncoding         eflag)
935     {
936       if( user.empty())
937       {
938         m_data->user = user;
939       }
940       else
941       {
942         if( config("with_authority") != "y")
943         {
944           ZYPP_THROW(UrlNotAllowedException(
945             _("Url scheme does not allow a username")
946           ));
947         }
948
949         if(eflag == zypp::url::E_ENCODED)
950         {
951           checkUrlData(user, "username", config("rx_username"));
952
953           m_data->user = user;
954         }
955         else
956         {
957           m_data->user = zypp::url::encode(
958             user, config("safe_username")
959           );
960         }
961       }
962     }
963
964
965     // ---------------------------------------------------------------
966     void
967     UrlBase::setPassword(const std::string &pass,
968                          EEncoding         eflag)
969     {
970       if( pass.empty())
971       {
972         m_data->pass = pass;
973       }
974       else
975       {
976         if( config("with_authority") != "y")
977         {
978           ZYPP_THROW(UrlNotAllowedException(
979             _("Url scheme does not allow a password")
980           ));
981         }
982
983         if(eflag == zypp::url::E_ENCODED)
984         {
985           checkUrlData(pass, "password", config("rx_password"), false);
986
987           m_data->pass = pass;
988         }
989         else
990         {
991           m_data->pass = zypp::url::encode(
992             pass, config("safe_password")
993           );
994         }
995       }
996     }
997
998
999     // ---------------------------------------------------------------
1000     void
1001     UrlBase::setHost(const std::string &host)
1002     {
1003       if( host.empty())
1004       {
1005         if(config("require_host") == "m")
1006         {
1007           ZYPP_THROW(UrlNotAllowedException(
1008             _("Url scheme requires a host component")
1009           ));
1010         }
1011         m_data->host = host;
1012       }
1013       else
1014       {
1015         if( config("with_authority") != "y")
1016         {
1017           ZYPP_THROW(UrlNotAllowedException(
1018             _("Url scheme does not allow a host component")
1019           ));
1020         }
1021
1022         if( isValidHost(host))
1023         {
1024           std::string temp;
1025
1026           // always decode in case isValidHost()
1027           // is reimplemented and supports also
1028           // the [v ... ] notation.
1029           if( host.at(0) == '[')
1030           {
1031             temp = str::toUpper(zypp::url::decode(host));
1032           }
1033           else
1034           {
1035             temp = str::toLower(zypp::url::decode(host));
1036           }
1037
1038           m_data->host = zypp::url::encode(
1039             temp, config("safe_hostname")
1040           );
1041         }
1042         else
1043         {
1044           ZYPP_THROW(UrlBadComponentException(
1045             str::form(_("Invalid host component '%s'"), host.c_str())
1046           ));
1047         }
1048       }
1049     }
1050
1051
1052     // ---------------------------------------------------------------
1053     void
1054     UrlBase::setPort(const std::string &port)
1055     {
1056       if( port.empty())
1057       {
1058         m_data->port = port;
1059       }
1060       else
1061       {
1062         if( config("with_authority") != "y" ||
1063             config("with_port")      != "y")
1064         {
1065           ZYPP_THROW(UrlNotAllowedException(
1066             _("Url scheme does not allow a port")
1067           ));
1068         }
1069
1070         if( isValidPort(port))
1071         {
1072           m_data->port = port;
1073         }
1074         else
1075         {
1076           ZYPP_THROW(UrlBadComponentException(
1077             str::form(_("Invalid port component '%s'"), port.c_str())
1078           ));
1079         }
1080       }
1081     }
1082
1083
1084     // ---------------------------------------------------------------
1085     void
1086     UrlBase::setPathName(const std::string &path,
1087                          EEncoding         eflag)
1088     {
1089       if( path.empty())
1090       {
1091         if(config("require_pathname") == "m")
1092         {
1093           ZYPP_THROW(UrlNotAllowedException(
1094             _("Url scheme requires path name")
1095           ));
1096         }
1097         m_data->pathname = path;
1098       }
1099       else
1100       {
1101         if(eflag == zypp::url::E_ENCODED)
1102         {
1103           checkUrlData(path, "path name", config("rx_pathname"));
1104
1105           if( !getHost(zypp::url::E_ENCODED).empty())
1106           {
1107             // has to begin with a "/". For consistency with
1108             // setPathName while the host is empty, we allow
1109             // it in encoded ("%2f") form - cleanupPathName()
1110             // will fix / decode the first slash if needed.
1111             if(!(path.at(0) == '/' || (path.size() >= 3 &&
1112                  str::toLower(path.substr(0, 3)) == "%2f")))
1113             {
1114               ZYPP_THROW(UrlNotAllowedException(
1115                 _("Relative path not allowed if authority exists")
1116               ));
1117             }
1118           }
1119
1120           m_data->pathname = cleanupPathName(path);
1121         }
1122         else //     zypp::url::E_DECODED
1123         {
1124           if( !getHost(zypp::url::E_ENCODED).empty())
1125           {
1126             if(path.at(0) != '/')
1127             {
1128               ZYPP_THROW(UrlNotAllowedException(
1129                 _("Relative path not allowed if authority exists")
1130               ));
1131             }
1132           }
1133
1134           m_data->pathname = cleanupPathName(
1135             zypp::url::encode(
1136               path, config("safe_pathname")
1137             )
1138           );
1139         }
1140       }
1141     }
1142
1143
1144     // ---------------------------------------------------------------
1145     void
1146     UrlBase::setPathParams(const std::string &params)
1147     {
1148       if( params.empty())
1149       {
1150         m_data->pathparams = params;
1151       }
1152       else
1153       {
1154         checkUrlData(params, "path parameters", config("rx_pathparams"));
1155
1156         m_data->pathparams = params;
1157       }
1158     }
1159
1160
1161     // ---------------------------------------------------------------
1162     void
1163     UrlBase::setPathParamsVec(const zypp::url::ParamVec &pvec)
1164     {
1165       setPathParams(
1166         zypp::url::join(
1167           pvec,
1168           config("psep_pathparam")
1169         )
1170       );
1171     }
1172
1173
1174     // ---------------------------------------------------------------
1175     void
1176     UrlBase::setPathParamsMap(const zypp::url::ParamMap &pmap)
1177     {
1178       if( config("psep_pathparam").empty() ||
1179           config("vsep_pathparam").empty())
1180       {
1181         ZYPP_THROW(UrlNotSupportedException(
1182           "Path Parameter parsing not supported for this URL"
1183         ));
1184       }
1185       setPathParams(
1186         zypp::url::join(
1187           pmap,
1188           config("psep_pathparam"),
1189           config("vsep_pathparam"),
1190           config("safe_pathparams")
1191         )
1192       );
1193     }
1194
1195
1196     // ---------------------------------------------------------------
1197     void
1198     UrlBase::setPathParam(const std::string &param, const std::string &value)
1199     {
1200           zypp::url::ParamMap pmap( getPathParamsMap(zypp::url::E_DECODED));
1201           pmap[param] = value;
1202           setPathParamsMap(pmap);
1203     }
1204
1205
1206     // ---------------------------------------------------------------
1207     void
1208     UrlBase::setQueryStringVec(const zypp::url::ParamVec &pvec)
1209     {
1210       setQueryString(
1211         zypp::url::join(
1212           pvec,
1213           config("psep_querystr")
1214         )
1215       );
1216     }
1217
1218
1219     // ---------------------------------------------------------------
1220     void
1221     UrlBase::setQueryStringMap(const zypp::url::ParamMap &pmap)
1222     {
1223       if( config("psep_querystr").empty() ||
1224           config("vsep_querystr").empty())
1225       {
1226         ZYPP_THROW(UrlNotSupportedException(
1227           _("Query string parsing not supported for this URL")
1228         ));
1229       }
1230       setQueryString(
1231         zypp::url::join(
1232           pmap,
1233           config("psep_querystr"),
1234           config("vsep_querystr"),
1235           config("safe_querystr")
1236         )
1237       );
1238     }
1239
1240     // ---------------------------------------------------------------
1241     void
1242     UrlBase::setQueryParam(const std::string &param, const std::string &value)
1243     {
1244           zypp::url::ParamMap pmap( getQueryStringMap(zypp::url::E_DECODED));
1245           pmap[param] = value;
1246           setQueryStringMap(pmap);
1247     }
1248
1249     // ---------------------------------------------------------------
1250     void
1251     UrlBase::delQueryParam(const std::string &param)
1252     {
1253           zypp::url::ParamMap pmap( getQueryStringMap(zypp::url::E_DECODED));
1254           pmap.erase(param);
1255           setQueryStringMap(pmap);
1256     }
1257
1258
1259     // ---------------------------------------------------------------
1260     std::string
1261     UrlBase::cleanupPathName(const std::string &path) const
1262     {
1263       bool authority = !getHost(zypp::url::E_ENCODED).empty();
1264       return cleanupPathName(path, authority);
1265     }
1266
1267     // ---------------------------------------------------------------
1268     std::string
1269     UrlBase::cleanupPathName(const std::string &path, bool authority) const
1270     {
1271       std::string copy( path);
1272
1273       // decode the first slash if it is encoded ...
1274       if(copy.size() >= 3 && copy.at(0) != '/' &&
1275          str::toLower(copy.substr(0, 3)) == "%2f")
1276       {
1277         copy.replace(0, 3, "/");
1278       }
1279
1280       // if path begins with a double slash ("//"); encode the second
1281       // slash [minimal and IMO sufficient] before the first path
1282       // segment, to fulfill the path-absolute rule of RFC 3986
1283       // disallowing a "//" if no authority is present.
1284       if( authority)
1285       {
1286         //
1287         // rewrite of "//" to "/%2f" not required, use config
1288         //
1289         if(config("path_encode_slash2") == "y")
1290         {
1291           // rewrite "//" ==> "/%2f"
1292           if(copy.size() >= 2 && copy.at(0) == '/' && copy.at(1) == '/')
1293           {
1294             copy.replace(1, 1, "%2F");
1295           }
1296         }
1297         else
1298         {
1299           // rewrite "/%2f" ==> "//"
1300           if(copy.size() >= 4 && copy.at(0) == '/' &&
1301              str::toLower(copy.substr(1, 4)) == "%2f")
1302           {
1303             copy.replace(1, 4, "/");
1304           }
1305         }
1306       }
1307       else
1308       {
1309         // rewrite of "//" to "/%2f" is required (no authority)
1310         if(copy.size() >= 2 && copy.at(0) == '/' && copy.at(1) == '/')
1311         {
1312           copy.replace(1, 1, "%2F");
1313         }
1314       }
1315       return copy;
1316     }
1317
1318
1319     // ---------------------------------------------------------------
1320     bool
1321     UrlBase::isValidHost(const std::string &host) const
1322     {
1323       try
1324       {
1325         str::regex regx(RX_VALID_HOSTIPV6);
1326         if( str::regex_match(host, regx))
1327         {
1328           struct in6_addr ip;
1329           std::string temp( host.substr(1, host.size()-2));
1330
1331           return inet_pton(AF_INET6, temp.c_str(), &ip) > 0;
1332         }
1333         else
1334         {
1335           // matches also IPv4 dotted-decimal adresses...
1336           std::string temp( zypp::url::decode(host));
1337           str::regex  regx(RX_VALID_HOSTNAME);
1338           return str::regex_match(temp, regx);
1339         }
1340       }
1341       catch( ... )
1342       {}
1343
1344       return false;
1345     }
1346
1347
1348     // ---------------------------------------------------------------
1349     bool
1350     UrlBase::isValidPort(const std::string &port) const
1351     {
1352       try
1353       {
1354         str::regex regx(RX_VALID_PORT);
1355         if( str::regex_match(port, regx))
1356         {
1357           long pnum = str::strtonum<long>(port);
1358           return ( pnum >= 1 && pnum <= USHRT_MAX);
1359         }
1360       }
1361       catch( ... )
1362       {}
1363       return false;
1364     }
1365
1366
1367     //////////////////////////////////////////////////////////////////
1368   } // namespace url
1369   ////////////////////////////////////////////////////////////////////
1370
1371   ////////////////////////////////////////////////////////////////////
1372 } // namespace zypp
1373 //////////////////////////////////////////////////////////////////////
1374 /*
1375 ** vim: set ts=2 sts=2 sw=2 ai et:
1376 */