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