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