Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / parser / RepoindexFileReader.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/parser/RepoindexFileReader.cc
10  * Implementation of repoindex.xml file reader.
11  */
12 #include <iostream>
13 #include <unordered_map>
14
15 #include "zypp/base/String.h"
16 #include "zypp/base/Logger.h"
17 #include "zypp/base/Gettext.h"
18 #include "zypp/base/InputStream.h"
19 #include "zypp/base/DefaultIntegral.h"
20
21 #include "zypp/Pathname.h"
22
23 #include "zypp/parser/xml/Reader.h"
24 #include "zypp/parser/ParseException.h"
25
26 #include "zypp/RepoInfo.h"
27
28 #include "zypp/parser/RepoindexFileReader.h"
29
30
31 #undef ZYPP_BASE_LOGGER_LOGGROUP
32 #define ZYPP_BASE_LOGGER_LOGGROUP "parser"
33
34 using std::endl;
35
36 namespace zypp
37 {
38   namespace parser
39   {
40     using xml::Reader;
41     using xml::XmlString;
42
43     ///////////////////////////////////////////////////////////////////
44     namespace
45     {
46       class VarReplacer : private base::NonCopyable
47       {
48       public:
49         /** */
50         void setVar( const std::string & key_r, const std::string & val_r )
51         {
52           //MIL << "*** Inject " << key_r << " = " << val_r;
53           _vars[key_r] = replace( val_r );
54           //MIL << " (" << _vars[key_r] << ")" << endl;
55         }
56
57         std::string replace( const std::string & val_r ) const
58         {
59           std::string::size_type vbeg = val_r.find( "%{", 0 );
60           if ( vbeg == std::string::npos )
61             return val_r;
62
63           str::Str ret;
64           std::string::size_type cbeg = 0;
65           for( ; vbeg != std::string::npos; vbeg = val_r.find( "%{", vbeg ) )
66           {
67             std::string::size_type nbeg = vbeg+2;
68             std::string::size_type nend = val_r.find( "}", nbeg );
69             if ( nend == std::string::npos )
70             {
71               WAR << "Incomplete variable in '" << val_r << "'" << endl;
72               break;
73             }
74             const auto & iter = _vars.find( val_r.substr( nbeg, nend-nbeg ) );
75             if ( iter != _vars.end() )
76             {
77               if ( cbeg < vbeg )
78                 ret << val_r.substr( cbeg, vbeg-cbeg );
79               ret << iter->second;
80               cbeg = nend+1;
81             }
82             else
83               WAR << "Undefined variable %{" << val_r.substr( nbeg, nend-nbeg ) << "} in '" << val_r << "'" << endl;
84             vbeg = nend+1;
85           }
86           if ( cbeg < val_r.size() )
87             ret << val_r.substr( cbeg );
88
89           return ret;
90         }
91       private:
92         std::unordered_map<std::string,std::string> _vars;
93       };
94     } // namespace
95     ///////////////////////////////////////////////////////////////////
96
97   ///////////////////////////////////////////////////////////////////////
98   //
99   //  CLASS NAME : RepoindexFileReader::Impl
100   //
101   class RepoindexFileReader::Impl : private base::NonCopyable
102   {
103   public:
104     /**
105      * CTOR
106      *
107      * \see RepoindexFileReader::RepoindexFileReader(Pathname,ProcessResource)
108      */
109     Impl(const InputStream &is, const ProcessResource & callback);
110
111     /**
112      * Callback provided to the XML parser.
113      */
114     bool consumeNode( Reader & reader_r );
115
116     DefaultIntegral<Date::Duration,0> _ttl;
117
118   private:
119     bool getAttrValue( const std::string & key_r, Reader & reader_r, std::string & value_r )
120     {
121       const XmlString & s( reader_r->getAttribute( key_r ) );
122       if ( s.get() )
123       {
124         value_r = _replacer.replace( s.asString() );
125         return !value_r.empty();
126       }
127       value_r.clear();
128       return false;
129     }
130
131   private:
132     /** Function for processing collected data. Passed-in through constructor. */
133     ProcessResource _callback;
134     VarReplacer _replacer;
135   };
136   ///////////////////////////////////////////////////////////////////////
137
138   RepoindexFileReader::Impl::Impl(const InputStream &is,
139                                   const ProcessResource & callback)
140     : _callback(callback)
141   {
142     Reader reader( is );
143     MIL << "Reading " << is.path() << endl;
144     reader.foreachNode( bind( &RepoindexFileReader::Impl::consumeNode, this, _1 ) );
145   }
146
147   // --------------------------------------------------------------------------
148
149   /*
150    * xpath and multiplicity of processed nodes are included in the code
151    * for convenience:
152    *
153    * // xpath: <xpath> (?|*|+)
154    *
155    * if multiplicity is ommited, then the node has multiplicity 'one'.
156    */
157
158   // --------------------------------------------------------------------------
159
160   bool RepoindexFileReader::Impl::consumeNode( Reader & reader_r )
161   {
162     if ( reader_r->nodeType() == XML_READER_TYPE_ELEMENT )
163     {
164       // xpath: /repoindex
165       if ( reader_r->name() == "repoindex" )
166       {
167         while ( reader_r.nextNodeAttribute() )
168         {
169           const std::string & name( reader_r->localName().asString() );
170           const std::string & value( reader_r->value().asString() );
171           _replacer.setVar( name, value );
172           // xpath: /repoindex@ttl
173           if ( name == "ttl" )
174             _ttl = str::strtonum<Date::Duration>(value);
175         }
176         return true;
177       }
178
179       // xpath: /repoindex/data (+)
180       if ( reader_r->name() == "repo" )
181       {
182         RepoInfo info;
183         // Set some defaults that are not contained in the repo information
184         info.setAutorefresh( true );
185         info.setEnabled(false);
186
187         std::string attrValue;
188
189         // required alias
190         // mandatory, so we can allow it in var replacement without reset
191         if ( getAttrValue( "alias", reader_r, attrValue ) )
192         {
193           info.setAlias( attrValue );
194           _replacer.setVar( "alias", attrValue );
195         }
196         else
197           throw ParseException(str::form(_("Required attribute '%s' is missing."), "alias"));
198
199         // required url
200         // SLES HACK: or path, but beware of the hardcoded '/repo' prefix!
201         {
202           std::string urlstr;
203           std::string pathstr;
204           getAttrValue( "url", reader_r, urlstr );
205           getAttrValue( "path", reader_r, pathstr );
206           if ( urlstr.empty() )
207           {
208             if ( pathstr.empty() )
209               throw ParseException(str::form(_("One or both of '%s' or '%s' attributes is required."), "url", "path"));
210             else
211               info.setPath( Pathname("/repo") / pathstr );
212           }
213           else
214           {
215             if ( pathstr.empty() )
216               info.setBaseUrl( Url(urlstr) );
217             else
218             {
219               Url url( urlstr );
220               url.setPathName( Pathname(url.getPathName()) / "repo" / pathstr );
221               info.setBaseUrl( url );
222             }
223           }
224         }
225
226         // optional name
227         if ( getAttrValue( "name", reader_r, attrValue ) )
228           info.setName( attrValue );
229
230         // optional targetDistro
231         if ( getAttrValue( "distro_target", reader_r, attrValue ) )
232           info.setTargetDistribution( attrValue );
233
234         // optional priority
235         if ( getAttrValue( "priority", reader_r, attrValue ) )
236           info.setPriority( str::strtonum<unsigned>( attrValue ) );
237
238
239         // optional enabled
240         if ( getAttrValue( "enabled", reader_r, attrValue ) )
241           info.setEnabled( str::strToBool( attrValue, info.enabled() ) );
242
243         // optional autorefresh
244         if ( getAttrValue( "autorefresh", reader_r, attrValue ) )
245           info.setAutorefresh( str::strToBool( attrValue, info.autorefresh() ) );
246
247         DBG << info << endl;
248
249         // ignore the rest
250         _callback(info);
251         return true;
252       }
253     }
254
255     return true;
256   }
257
258
259   ///////////////////////////////////////////////////////////////////
260   //
261   //  CLASS NAME : RepoindexFileReader
262   //
263   ///////////////////////////////////////////////////////////////////
264
265   RepoindexFileReader::RepoindexFileReader( const Pathname & repoindex_file, const ProcessResource & callback )
266   : _pimpl(new Impl(InputStream(repoindex_file), callback))
267   {}
268
269   RepoindexFileReader::RepoindexFileReader( const InputStream &is, const ProcessResource & callback )
270   : _pimpl(new Impl(is, callback))
271   {}
272
273   RepoindexFileReader::~RepoindexFileReader()
274   {}
275
276   Date::Duration RepoindexFileReader::ttl() const       { return _pimpl->_ttl; }
277
278   } // ns parser
279 } // ns zypp
280
281 // vim: set ts=2 sts=2 sw=2 et ai: