Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / parser / RepoindexFileReader.cc
index 3ec50e3..5414d0c 100644 (file)
  * Implementation of repoindex.xml file reader.
  */
 #include <iostream>
+#include <unordered_map>
 
 #include "zypp/base/String.h"
 #include "zypp/base/Logger.h"
-
-#include "zypp/RepoInfo.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/InputStream.h"
+#include "zypp/base/DefaultIntegral.h"
 
 #include "zypp/Pathname.h"
+
 #include "zypp/parser/xml/Reader.h"
 #include "zypp/parser/ParseException.h"
 
+#include "zypp/RepoInfo.h"
+
 #include "zypp/parser/RepoindexFileReader.h"
 
 
 #undef ZYPP_BASE_LOGGER_LOGGROUP
 #define ZYPP_BASE_LOGGER_LOGGROUP "parser"
 
-using namespace std;
-using namespace zypp::xml;
+using std::endl;
 
 namespace zypp
 {
   namespace parser
   {
+    using xml::Reader;
+    using xml::XmlString;
 
+    ///////////////////////////////////////////////////////////////////
+    namespace
+    {
+      class VarReplacer : private base::NonCopyable
+      {
+      public:
+       /** */
+       void setVar( const std::string & key_r, const std::string & val_r )
+       {
+         //MIL << "*** Inject " << key_r << " = " << val_r;
+         _vars[key_r] = replace( val_r );
+         //MIL << " (" << _vars[key_r] << ")" << endl;
+       }
+
+       std::string replace( const std::string & val_r ) const
+       {
+         std::string::size_type vbeg = val_r.find( "%{", 0 );
+         if ( vbeg == std::string::npos )
+           return val_r;
+
+         str::Str ret;
+         std::string::size_type cbeg = 0;
+         for( ; vbeg != std::string::npos; vbeg = val_r.find( "%{", vbeg ) )
+         {
+           std::string::size_type nbeg = vbeg+2;
+           std::string::size_type nend = val_r.find( "}", nbeg );
+           if ( nend == std::string::npos )
+           {
+             WAR << "Incomplete variable in '" << val_r << "'" << endl;
+             break;
+           }
+           const auto & iter = _vars.find( val_r.substr( nbeg, nend-nbeg ) );
+           if ( iter != _vars.end() )
+           {
+             if ( cbeg < vbeg )
+               ret << val_r.substr( cbeg, vbeg-cbeg );
+             ret << iter->second;
+             cbeg = nend+1;
+           }
+           else
+             WAR << "Undefined variable %{" << val_r.substr( nbeg, nend-nbeg ) << "} in '" << val_r << "'" << endl;
+           vbeg = nend+1;
+         }
+         if ( cbeg < val_r.size() )
+           ret << val_r.substr( cbeg );
+
+         return ret;
+       }
+      private:
+       std::unordered_map<std::string,std::string> _vars;
+      };
+    } // namespace
+    ///////////////////////////////////////////////////////////////////
 
   ///////////////////////////////////////////////////////////////////////
   //
@@ -47,26 +106,41 @@ namespace zypp
      *
      * \see RepoindexFileReader::RepoindexFileReader(Pathname,ProcessResource)
      */
-    Impl(const Pathname &repoindex_file, const ProcessResource & callback);
+    Impl(const InputStream &is, const ProcessResource & callback);
 
     /**
      * Callback provided to the XML parser.
      */
     bool consumeNode( Reader & reader_r );
 
+    DefaultIntegral<Date::Duration,0> _ttl;
+
+  private:
+    bool getAttrValue( const std::string & key_r, Reader & reader_r, std::string & value_r )
+    {
+      const XmlString & s( reader_r->getAttribute( key_r ) );
+      if ( s.get() )
+      {
+       value_r = _replacer.replace( s.asString() );
+       return !value_r.empty();
+      }
+      value_r.clear();
+      return false;
+    }
 
   private:
     /** Function for processing collected data. Passed-in through constructor. */
     ProcessResource _callback;
+    VarReplacer _replacer;
   };
   ///////////////////////////////////////////////////////////////////////
 
-  RepoindexFileReader::Impl::Impl(
-      const Pathname &repoindex_file, const ProcessResource & callback)
+  RepoindexFileReader::Impl::Impl(const InputStream &is,
+                                  const ProcessResource & callback)
     : _callback(callback)
   {
-    Reader reader( repoindex_file );
-    MIL << "Reading " << repoindex_file << endl;
+    Reader reader( is );
+    MIL << "Reading " << is.path() << endl;
     reader.foreachNode( bind( &RepoindexFileReader::Impl::consumeNode, this, _1 ) );
   }
 
@@ -90,47 +164,85 @@ namespace zypp
       // xpath: /repoindex
       if ( reader_r->name() == "repoindex" )
       {
+       while ( reader_r.nextNodeAttribute() )
+       {
+         const std::string & name( reader_r->localName().asString() );
+         const std::string & value( reader_r->value().asString() );
+         _replacer.setVar( name, value );
+         // xpath: /repoindex@ttl
+         if ( name == "ttl" )
+           _ttl = str::strtonum<Date::Duration>(value);
+       }
         return true;
       }
 
       // xpath: /repoindex/data (+)
       if ( reader_r->name() == "repo" )
       {
-        XmlString s;
         RepoInfo info;
-
-        // url and/or path
-        string url_s;
-        s = reader_r->getAttribute("url");
-        if (s.get())
-          url_s = s.asString();
-        string path_s;
-        s = reader_r->getAttribute("path");
-        if (s.get())
-          path_s = s.asString();
-
-        if (url_s.empty() && path_s.empty())
-          throw ParseException(_("One or both of 'url' or 'path' attributes is required."));
-        //! \todo FIXME this hardcodes the "/repo/" fragment - should not be if we want it to be usable by others!
-        else if (url_s.empty())
-          info.setPath(Pathname(string("/repo/") + path_s));
-        else if (path_s.empty())
-          info.setBaseUrl(Url(url_s));
-        else
-          info.setBaseUrl(Url(url_s + "/repo/" + path_s));
-
-        // required alias
-        info.setAlias(reader_r->getAttribute("alias").asString());
-
-        // optional type
-        s = reader_r->getAttribute("type");
-        if (s.get())
-          info.setType(repo::RepoType(s.asString()));
+        // Set some defaults that are not contained in the repo information
+        info.setAutorefresh( true );
+       info.setEnabled(false);
+
+       std::string attrValue;
+
+       // required alias
+       // mandatory, so we can allow it in var replacement without reset
+       if ( getAttrValue( "alias", reader_r, attrValue ) )
+       {
+         info.setAlias( attrValue );
+         _replacer.setVar( "alias", attrValue );
+       }
+       else
+         throw ParseException(str::form(_("Required attribute '%s' is missing."), "alias"));
+
+        // required url
+       // SLES HACK: or path, but beware of the hardcoded '/repo' prefix!
+       {
+         std::string urlstr;
+         std::string pathstr;
+         getAttrValue( "url", reader_r, urlstr );
+         getAttrValue( "path", reader_r, pathstr );
+         if ( urlstr.empty() )
+         {
+           if ( pathstr.empty() )
+             throw ParseException(str::form(_("One or both of '%s' or '%s' attributes is required."), "url", "path"));
+           else
+             info.setPath( Pathname("/repo") / pathstr );
+         }
+         else
+         {
+           if ( pathstr.empty() )
+             info.setBaseUrl( Url(urlstr) );
+           else
+           {
+             Url url( urlstr );
+             url.setPathName( Pathname(url.getPathName()) / "repo" / pathstr );
+             info.setBaseUrl( url );
+           }
+         }
+       }
 
         // optional name
-        s = reader_r->getAttribute("name");
-        if (s.get())
-          info.setName(s.asString());
+        if ( getAttrValue( "name", reader_r, attrValue ) )
+          info.setName( attrValue );
+
+        // optional targetDistro
+        if ( getAttrValue( "distro_target", reader_r, attrValue ) )
+          info.setTargetDistribution( attrValue );
+
+        // optional priority
+        if ( getAttrValue( "priority", reader_r, attrValue ) )
+          info.setPriority( str::strtonum<unsigned>( attrValue ) );
+
+
+        // optional enabled
+        if ( getAttrValue( "enabled", reader_r, attrValue ) )
+          info.setEnabled( str::strToBool( attrValue, info.enabled() ) );
+
+        // optional autorefresh
+       if ( getAttrValue( "autorefresh", reader_r, attrValue ) )
+         info.setAutorefresh( str::strToBool( attrValue, info.autorefresh() ) );
 
         DBG << info << endl;
 
@@ -150,15 +262,18 @@ namespace zypp
   //
   ///////////////////////////////////////////////////////////////////
 
-  RepoindexFileReader::RepoindexFileReader(
-      const Pathname & repoindex_file, const ProcessResource & callback)
-    :
-      _pimpl(new Impl(repoindex_file, callback))
+  RepoindexFileReader::RepoindexFileReader( const Pathname & repoindex_file, const ProcessResource & callback )
+  : _pimpl(new Impl(InputStream(repoindex_file), callback))
+  {}
+
+  RepoindexFileReader::RepoindexFileReader( const InputStream &is, const ProcessResource & callback )
+  : _pimpl(new Impl(is, callback))
   {}
 
   RepoindexFileReader::~RepoindexFileReader()
   {}
 
+  Date::Duration RepoindexFileReader::ttl() const      { return _pimpl->_ttl; }
 
   } // ns parser
 } // ns zypp